So I am using pyCollada to try to export multiple meshes to the same scene. Alas, whenever I try to do so, I can only see one of the meshes I have loaded in. Am I doing something wrong when I create the file? Each individual mesh renders perfectly if I separate them into their own file, but they fail when I attempt to output them to the same file. I have looked through the API, but the documentation is very limited. Any help would be appreciated.
My code is listed shown below.
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 12 14:43:05 2015
#author: skylion
"""
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 11 11:01:48 2015
#author: danaukes
"""
import sys
import popupcad_deprecated
import popupcad_manufacturing_plugins
import popupcad
from popupcad.filetypes.design import Design
import PySide.QtGui as qg
#Draws Collada stuff
from collada import *
import numpy
geom_index = 0;
def exportBodyToMesh(output):
# csg = output.csg
generic = output.generic_laminate()
# layers = generic.layers()
layerdef = d.return_layer_definition()
layerdef.refreshzvalues()
# layers = layerdef.layers
mesh = Collada()
nodes = []
for layer in layerdef.layers:
shapes = generic.geoms[layer]#TODO Add it in for other shapes
zvalue = layerdef.zvalue[layer]
height = zvalue * 1/ popupcad.internal_argument_scaling
print zvalue
if (len(shapes) == 0) : #In case there are no shapes.
print "No shapes skipping"
continue
print shapes
for s in shapes:
geom = createMeshFromShape(s, height, mesh)
mesh.geometries.append(geom)
effect = material.Effect("effect" + str(geom_index), [], "phone", diffuse=(1,0,0), specular=(0,1,0))
mat = material.Material("material" + str(geom_index), "mymaterial", effect)
matnode = scene.MaterialNode("materialref" + str(geom_index), mat, inputs=[])
mesh.effects.append(effect)
mesh.materials.append(mat)
geomnode = scene.GeometryNode(geom, [matnode])
node = scene.Node("node" + str(geom_index), children=[geomnode])
nodes.append(node)
print nodes
myscene = scene.Scene("myscene", nodes)
mesh.scenes.append(myscene)
mesh.scene = myscene
# layer_num = layer_num + 1 #Add the layer thicknes instead of simply + 1
filename = str(output) + '.dae'
mesh.write(filename)
#TODO Add handling in case rigid body has already been selected.
print filename + " has been saved"
def createMeshFromShape(s,layer_num, mesh):
s.exteriorpoints()
a = s.triangles3()
vertices = []
global geom_index
for coord in a:
for dec in coord:
vertices.append(dec[0]) #x-axis
vertices.append(dec[1]) #y-axis
vertices.append(layer_num ) #z-axi
#This scales the verticies properly.
vert_floats = [x/popupcad.internal_argument_scaling for x in vertices]
vert_src = source.FloatSource("cubeverts-array" + str(geom_index), numpy.array(vert_floats), ('X', 'Y', 'Z'))
geom = geometry.Geometry(mesh, "geometry" + str(geom_index), "mycube", [vert_src])
input_list = source.InputList()
input_list.addInput(0, 'VERTEX', "#cubeverts-array" + str(geom_index))
indices = numpy.array(range(0,(len(vertices) / 3)));
triset = geom.createTriangleSet(indices, input_list, "materialref")
geom_index += 1
triset.generateNormals()
geom.primitives.append(triset)
return geom
#Start of actual script
print sys.argv
app = qg.QApplication('exporter.py')
d = Design.open()
print "Loading..."
d.reprocessoperations()
operation = d.operations[3] #Identify bodies
for output in operation.output:
exportBodyToMesh(output)
print "All objects printed"
#sys.exit(app.exec_())
Your code to add the geometry to the scene is outside your inner loop. You're only adding the last geometry to the scene, rather than all of them. You should be creating multiple GeometryNode and adding all of them to the Scene.
Related
I'm trying to create 3D model of the skull using VTK [example]:(https://kitware.github.io/vtk-examples/site/Python/VisualizationAlgorithms/HeadBone/)
#!/usr/bin/env python
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import (
VTK_VERSION_NUMBER,
vtkVersion
)
from vtkmodules.vtkCommonDataModel import vtkMergePoints
from vtkmodules.vtkFiltersCore import (
vtkFlyingEdges3D,
vtkMarchingCubes
)
from vtkmodules.vtkFiltersModeling import vtkOutlineFilter
from vtkmodules.vtkIOImage import vtkMetaImageReader
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
# vtkFlyingEdges3D was introduced in VTK >= 8.2
use_flying_edges = vtk_version_ok(8, 2, 0)
file_name = get_program_parameters()
colors = vtkNamedColors()
# Create the RenderWindow, Renderer and Interactor.
ren = vtkRenderer()
ren_win = vtkRenderWindow()
ren_win.AddRenderer(ren)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
# Create the pipeline.
reader = vtkMetaImageReader()
reader.SetFileName(file_name)
reader.Update()
locator = vtkMergePoints()
locator.SetDivisions(64, 64, 92)
locator.SetNumberOfPointsPerBucket(2)
locator.AutomaticOff()
if use_flying_edges:
try:
using_marching_cubes = False
iso = vtkFlyingEdges3D()
except AttributeError:
using_marching_cubes = True
iso = vtkMarchingCubes()
else:
using_marching_cubes = True
iso = vtkMarchingCubes()
iso.SetInputConnection(reader.GetOutputPort())
iso.ComputeGradientsOn()
iso.ComputeScalarsOff()
iso.SetValue(0, 1150)
if using_marching_cubes:
iso.SetLocator(locator)
iso_mapper = vtkPolyDataMapper()
iso_mapper.SetInputConnection(iso.GetOutputPort())
iso_mapper.ScalarVisibilityOff()
iso_actor = vtkActor()
iso_actor.SetMapper(iso_mapper)
iso_actor.GetProperty().SetColor(colors.GetColor3d('Ivory'))
outline = vtkOutlineFilter()
outline.SetInputConnection(reader.GetOutputPort())
outline_mapper = vtkPolyDataMapper()
outline_mapper.SetInputConnection(outline.GetOutputPort())
outline_actor = vtkActor()
outline_actor.SetMapper(outline_mapper)
# Add the actors to the renderer, set the background and size.
#
ren.AddActor(outline_actor)
ren.AddActor(iso_actor)
ren.SetBackground(colors.GetColor3d('SlateGray'))
ren.GetActiveCamera().SetFocalPoint(0, 0, 0)
ren.GetActiveCamera().SetPosition(0, -1, 0)
ren.GetActiveCamera().SetViewUp(0, 0, -1)
ren.ResetCamera()
ren.GetActiveCamera().Dolly(1.5)
ren.ResetCameraClippingRange()
ren_win.SetSize(640, 480)
ren_win.SetWindowName('HeadBone')
ren_win.Render()
iren.Start()
def get_program_parameters():
import argparse
description = 'Marching cubes surface of human bone.'
epilogue = '''
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('filename', help='FullHead.mhd.')
args = parser.parse_args()
return args.filename
def vtk_version_ok(major, minor, build):
"""
Check the VTK version.
:param major: Major version.
:param minor: Minor version.
:param build: Build version.
:return: True if the requested VTK version is greater or equal to the actual VTK version.
"""
needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build)
try:
vtk_version_number = VTK_VERSION_NUMBER
except AttributeError: # as error:
ver = vtkVersion()
vtk_version_number = 10000000000 * ver.GetVTKMajorVersion() + 100000000 * ver.GetVTKMinorVersion() \
+ ver.GetVTKBuildVersion()
if vtk_version_number >= needed_version:
return True
else:
return False
if __name__ == '__main__':
main()
It appears that model works just fine, but there is something that I want to know. The model of the skull works perfectly fine, but the model density is not solid (it hollow inside). It generates only the surface of the model.
I want to know how I can fill the gap in the surface to get the full solid model.
In this example, you are trying to extract the isosurface (hollow) from the data.
vtkFlyingEdges3D() and vtkMarchingCubes() algorithms will create contours based on the iso.SetValue(0, 1150) and extract the surface for you. If you want to keep it filled, remove the contouring functions, i'e, vtkFlyingEdges3D() and vtkMarchingCubes() from your script and use some image data mapper instead of vtkPolydataMapper, it will show the entire object.
I also posted in the pyqtgraph forum here.
My overall goal is to have several clickable regions overlaid on an image, and if the plot boundary of any region is clicked I get a signal with the ID of that region. Something like this:
If I use only one PlotDataItem with nan-separated curves then each boundary sends the same signal. However, using a separate PlotDataItem for each boundary makes the application extremely sluggish.
I ended up subclassing ScatterPlotItem and rewriting the pointsAt function, which does what I want. The problem now is I can't figure out the appropriate way to change the ScatterPlotItem's boundingRect. Am I on the right approach? Is there a better way of doing this?
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
class CustScatter(pg.ScatterPlotItem):
def pointsAt(self, pos: QtCore.QPointF):
"""
The default implementation only checks a square around each spot. However, this is not
precise enough for my needs. It also triggers when clicking *inside* the spot boundary,
which I don't want.
"""
pts = []
for spot in self.points(): # type: pg.SpotItem
symb = QtGui.QPainterPath(spot.symbol())
symb.translate(spot.pos())
stroker = QtGui.QPainterPathStroker()
mousePath = stroker.createStroke(symb)
# Only trigger when clicking a boundary, not the inside of the shape
if mousePath.contains(pos):
pts.append(spot)
return pts[::-1]
"""Make some sample data"""
tri = np.array([[0,2.3,0,1,4,5,0], [0,4,4,8,8,3,0]]).T
tris = []
xyLocs = []
datas = []
for ii in np.arange(0, 16, 5):
curTri = tri + ii
tris.append(curTri)
xyLocs.append(curTri.min(0))
datas.append(ii)
def ptsClicked(item, pts):
print(f'ID {pts[0].data()} Clicked!')
"""Logic for making spot shapes from a list of (x,y) vertices"""
def makeSymbol(verts: np.ndarray):
outSymbol = QtGui.QPainterPath()
symPath = pg.arrayToQPath(*verts.T)
outSymbol.addPath(symPath)
# From pyqtgraph.examples for plotting text
br = outSymbol.boundingRect()
tr = QtGui.QTransform()
tr.translate(-br.x(), -br.y())
outSymbol = tr.map(outSymbol)
return outSymbol
app = pg.mkQApp()
pg.setConfigOption('background', 'w')
symbs = []
for xyLoc, tri in zip(xyLocs, tris):
symbs.append(makeSymbol(tri))
"""Create the scatterplot"""
xyLocs = np.vstack(xyLocs)
tri2 = pg.PlotDataItem()
scat = CustScatter(*xyLocs.T, symbol=symbs, data=datas, connect='finite',
pxMode=False, brush=None, pen=pg.mkPen(width=5), size=1)
scat.sigClicked.connect(ptsClicked)
# Now each 'point' is one of the triangles, hopefully
"""Construct GUI window"""
w = pg.PlotWindow()
w.plotItem.addItem(scat)
plt: pg.PlotItem = w.plotItem
plt.showGrid(True, True, 1)
w.show()
app.exec()
Solved! It turns out unless you specify otherwise, the boundingRect of each symbol in the dataset is assumed to be 1 and that the spot size is the limiting factor. After overriding measureSpotSizes as well my solution works:
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
class CustScatter(pg.ScatterPlotItem):
def pointsAt(self, pos: QtCore.QPointF):
"""
The default implementation only checks a square around each spot. However, this is not
precise enough for my needs. It also triggers when clicking *inside* the spot boundary,
which I don't want.
"""
pts = []
for spot in self.points(): # type: pg.SpotItem
symb = QtGui.QPainterPath(spot.symbol())
symb.translate(spot.pos())
stroker = QtGui.QPainterPathStroker()
mousePath = stroker.createStroke(symb)
# Only trigger when clicking a boundary, not the inside of the shape
if mousePath.contains(pos):
pts.append(spot)
return pts[::-1]
def measureSpotSizes(self, dataSet):
"""
Override the method so that it takes symbol size into account
"""
for rec in dataSet:
## keep track of the maximum spot size and pixel size
symbol, size, pen, brush = self.getSpotOpts(rec)
br = symbol.boundingRect()
size = max(br.width(), br.height())*2
width = 0
pxWidth = 0
if self.opts['pxMode']:
pxWidth = size + pen.widthF()
else:
width = size
if pen.isCosmetic():
pxWidth += pen.widthF()
else:
width += pen.widthF()
self._maxSpotWidth = max(self._maxSpotWidth, width)
self._maxSpotPxWidth = max(self._maxSpotPxWidth, pxWidth)
self.bounds = [None, None]
"""Make some sample data"""
tri = np.array([[0,2.3,0,1,4,5,0], [0,4,4,8,8,3,0]]).T
tris = []
xyLocs = []
datas = []
for ii in np.arange(0, 16, 5):
curTri = tri + ii
tris.append(curTri)
xyLocs.append(curTri.min(0))
datas.append(ii)
def ptsClicked(item, pts):
print(f'ID {pts[0].data()} Clicked!')
"""Logic for making spot shapes from a list of (x,y) vertices"""
def makeSymbol(verts: np.ndarray):
plotVerts = verts - verts.min(0, keepdims=True)
symPath = pg.arrayToQPath(*plotVerts.T)
return symPath
app = pg.mkQApp()
pg.setConfigOption('background', 'd')
symbs = []
for xyLoc, tri in zip(xyLocs, tris):
symbs.append(makeSymbol(tri))
"""Create the scatterplot"""
xyLocs = np.vstack(xyLocs)
tri2 = pg.PlotDataItem()
scat = CustScatter(*xyLocs.T, symbol=symbs, data=datas, connect='finite',
pxMode=False, brush=None, pen=pg.mkPen(width=5), size=1)
scat.sigClicked.connect(ptsClicked)
# Now each 'point' is one of the triangles, hopefully
"""Construct GUI window"""
w = pg.PlotWindow()
w.plotItem.addItem(scat)
plt: pg.PlotItem = w.plotItem
plt.showGrid(True, True, 1)
w.show()
app.exec()
I want to convert an xlsx with Python. I used the modules tablib and xtopdf to build a well structured table. Works excellent! Unfortunately the content does not fit on one pdf page. So I wanted to change the pagesize and format to horizontal A3. But I don't know how that could work. My code:
import random
import tablib
from openpyxl import load_workbook
from xtopdf import PDFWriter
from pyPdf import PdfFileWriter, PdfFileReader
workbook = load_workbook('C:/Users/user1/Testexcel.xlsx', guess_types=True, data_only=True)
worksheet = workbook.get_sheet_by_name('Testsheet')
ws_range = worksheet.iter_rows('A4:H6')
# Helper function to output a string to both screen and PDF.
def print_and_write(pw, strng):
print strng
pw.writeLine(strng)
# Create an empty Dataset and set its headers.
data = tablib.Dataset()
data.headers = ['col1', 'col2', 'col3', 'col4']
widths = [30, 20, 10, 20] # Display widths for columns.
for row in ws_range:
col1 = str(row[0].value)
col2 = str(row[1].value)
col3 = str(row[2].value)
col4 = str(row[3].value)
columns = [col1, col2, col3, col4]
row = [ str(col).center(widths[idx]) for idx, col in enumerate(columns) ]
data.append(row)
# Set up the PDFWriter.
pw = PDFWriter('C:/Users/user1/Test.pdf')
pw.setFont('Courier', 10)
pw.setHeader('Test')
pw.setFooter('Test')
# Generate header and data rows as strings; output them to screen and PDF.
separator = '-' * sum(widths)
print_and_write(pw, separator)
# Output headers
header_strs = [ header.center(widths[idx]) for idx, header in enumerate(data.headers) ]
print_and_write(pw, ''.join(header_strs))
print_and_write(pw, separator)
# Output data
for row in data:
print_and_write(pw, ''.join(row))
print_and_write(pw, separator)
pw.close()
Found out that the PDFWriter from xtopdf itself instanciates an canvas object of the reportlab library. In the canvas class an attribute pagesize is declared which is setted by default to 'A4'. But if I change the entry to 'A3' the result pdf still is in 'A4'.
class Canvas(textobject._PDFColorSetter):
from reportlab.pdfgen import canvas
c = canvas.Canvas("hello.pdf")
from reportlab.lib.units import inch
# move the origin up and to the left
c.translate(inch,inch)
# define a large font
c.setFont("Helvetica", 80)
# choose some colors
c.setStrokeColorRGB(0.2,0.5,0.3)
c.setFillColorRGB(1,0,1)
# draw a rectangle
c.rect(inch,inch,6*inch,9*inch, fill=1)
# make text go straight up
c.rotate(90)
# change color
c.setFillColorRGB(0,0,0.77)
# say hello (note after rotate the y coord needs to be negative!)
c.drawString(3*inch, -3*inch, "Hello World")
c.showPage()
c.save()
"""
def __init__(self,filename,
pagesize='A3',
bottomup = 1,
pageCompression=None,
encoding = None,
invariant = None,
verbosity=0):
"""Create a canvas of a given size. etc.
You may pass a file-like object to filename as an alternative to
a string.
Most of the attributes are private - we will use set/get methods
as the preferred interface. Default page size is A4."""
if pagesize is None: pagesize = 'A3'
if encoding is None: encoding = rl_config.defaultEncoding
if invariant is None: invariant = rl_config.invariant
self._filename = filename
self._encodingName = encoding
self._doc = pdfdoc.PDFDocument(encoding,
compression=pageCompression,
invariant=invariant, filename=filename)
#this only controls whether it prints 'saved ...' - 0 disables
self._verbosity = verbosity
#this is called each time a page is output if non-null
self._onPage = None
self._pagesize = pagesize
self._pageRotation = 0
#self._currentPageHasImages = 0
self._pageTransition = None
self._pageDuration = None
self._destinations = {} # dictionary of destinations for cross indexing.
self.setPageCompression(pageCompression)
self._pageNumber = 1 # keep a count
#self3 = [] #where the current page's marking operators accumulate
# when we create a form we need to save operations not in the form
self._codeStack = []
self._restartAccumulators() # restart all accumulation state (generalized, arw)
self._annotationCount = 0
self._outlines = [] # list for a name tree
self._psCommandsBeforePage = [] #for postscript tray/font commands
self._psCommandsAfterPage = [] #for postscript tray/font commands
#PostScript has the origin at bottom left. It is easy to achieve a top-
#down coord system by translating to the top of the page and setting y
#scale to -1, but then text is inverted. So self.bottomup is used
#to also set the text matrix accordingly. You can now choose your
#drawing coordinates.
self.bottomup = bottomup
self.imageCaching = rl_config.defaultImageCaching
self._make_preamble()
self.init_graphics_state()
self.state_stack = []
edit: I think the changes in the reportlab module are not accepted by the system. Tried to remove the dictionary reportlab and tried to import it then in the commandline. Ironically it works ylthough python should not find that module anymore.
try this
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
c = canvas.Canvas("hello.pdf", pagesize = (297 * mm, 420 * mm))
# or (420 * mm, 297 * mm) if you want it in portrait format
# values for inch: 11.69 * inch , 16.53 * inch
#the following would create an empty page
c.showPage()
c.save()
Just forked a project named xtopdf at bitbucket and made the following change:
##------------------------ PDFWriter.__init__ ----------------------------
- def __init__(self, pdf_fn):
+ def __init__(self, pdf_fn, pagesize='A4'):
'''
Constructor.
"pdf_fn" arg is the name of the PDF file to be created.
'''
self.__pdf_fn = pdf_fn # file name of PDF file
- self.__canv = canvas.Canvas(pdf_fn) # canvas to write on
+ self.__canv = canvas.Canvas(pdf_fn, pagesize) # canvas to write on
self.__font_name = None # font name
self.__font_size = None # font size
self.__header_str = None # header string (partial)
Can you try it? use pw = PDFWriter('C:/Users/user1/Test.pdf', 'A3').
I am creating my own .obj exporter for maya.
When i'm exporting just one mesh my code works just fine but when exporting several meshes / objects it fails to create the complete meshes.
I'm pretty sure that the problem is when i'm getting the
face.getVertices(), face.getUVIndex() and face.normalIndex() and printing them to the file. As i said the first mesh works fine but when it gets to the second mesh the codinates gets all wrong, they connect to the wrong triangles.
If anyone has any ideas on how to possibly loop them differently or change the values to the correct ones i would be forever greatful. Help would be very very appreciated!
Here is an example on how a multi object mesh ends out.
http://postimg.org/image/rr0fvs0v7/
import pymel.core as pm
import pymel.core.nodetypes as nt
planes = pm.ls(sl=True)
def meshFile():
def myRound(n):
return round(n, 6)
file = open("C:/Users/Blondiegirls/Desktop/test2.obj", "wb")
file.write("mtllib test2.mtl\r\n")
for p in planes[:]:
#pm.polyTriangulate(planes[0])
file.write("\r\ng default")
# Printa world kordinater
for index, point in enumerate(p.vtx):
temp = index,map(myRound, point.getPosition(space='world'))
file.write("\r\nv ")
file.write(str(' '.join(map(str, temp[1]))))
# Printa texture kordinater
mesh = pm.ls(g=True)[0]
U,V = mesh.getUVs()
UVs = zip(U,V)
for uv in UVs:
file.write("\r\nvt ")
file.write(str(uv[0])+" "+str(uv[1]))
#printa normals
for n in p.getNormals():
file.write("\r\nvn ")
file.write(str(n[0])+" "+str(n[1])+" "+str(n[2]))
file.write("\r\ns 1")
file.write("\r\ng ")
file.write(str(p))
file.write("\r\nusemtl test")
for faceIndex, face in enumerate(p.faces):
faceVertices = face.getVertices()
faceUV0 = face.getUVIndex(0)+1
faceUV1 = face.getUVIndex(1)+1
faceUV2 = face.getUVIndex(2)+1
faceNor0 = face.normalIndex(0)+1
faceNor1 = face.normalIndex(1)+1
faceNor2 = face.normalIndex(2)+1
file.write("\r\nf ")
faceVertices0 = int(faceVertices[0])+1
faceVertices1 = int(faceVertices[1])+1
faceVertices2 = int(faceVertices[2])+1
temp3 = (str(faceVertices0)) + "/" + (str(faceUV0)) +"/" + (str(faceNor0)) + " " + (str(faceVertices1)) + "/" + (str(faceUV1)) +"/" + (str(faceNor1)) + " " + (str(faceVertices2)) + "/" + (str(faceUV2)) +"/" + (str(faceNor2))
file.write(str(temp3))
file.close()
meshFile()
def MTLFile():
file2 = open("C:/Users/Blondiegirls/Desktop/test2.mtl", "wb")
object = cmds.ls(sl=1)[0].split(':')[0]
#print('object: '+object)
shipTX = pm.PyNode(object)
shadingGroups = shipTX.shadingGroups()
sg1 = shadingGroups[0]
material = sg1.listConnections(source=True, destination=False, type=nt.Lambert)[0]
file = material.color.listConnections(type=nt.File)[0]
filename = file.fileTextureName.get()
materialColor = material.getColor() #for Kd
materialAmbient = material.getAmbientColor() #for Ka
materialSpecular = material.getSpecularColor() #for Ks
refractiveIndex = material.getRefractiveIndex() #for Ni
file2.write("newmtl "+"test"+"\r\n")
file2.write("Ka "+str(materialAmbient[0])+" "+str(materialAmbient[1])+" "+str(materialAmbient[2])+"\r\n")
file2.write("Kd "+str(materialColor[0])+" "+str(materialColor[1])+" "+str(materialColor[2])+"\r\n")
file2.write("Ks "+str(materialSpecular[0])+" "+str(materialSpecular[1])+" "+str(materialSpecular[2])+"\r\n")
file2.write("d 1.0\r\n")
file2.write("Illum 2\r\n")
file2.write("map_Kd "+filename+"\r\n") #for map_Kd
file2.close()
MTLFile()
The problem is this line:
mesh = pm.ls(g=True)[0]
U,V = mesh.getUVs()
This is querying all of the geometry in the scene and returning the first object, but you should be operating on the current mesh in the iteration. I think what you want is:
U,V = p.getUVs()
Also, you should probably consider adding an argument to meshFile() rather than relying on a variable in the global scope.
I created a python program which uses the function "CostPath" of ArcGIS to automatically build least-cost paths (LCPs) between several polygons contained in the shapefile "selected_patches.shp". My python program seems to work but it is much too slow. I must build 275493 LCPs. Unfortunately, I don't know how to speed up my program (I am a beginner in Python programming language and ArcGIS). Or is there another solution to calculate rapidly least-cost paths between several polygons with ArcGIS (I use ArcGIS 10.1) ? Here is my code:
# Import system modules
import arcpy
from arcpy import env
from arcpy.sa import *
arcpy.CheckOutExtension("Spatial")
# Overwrite outputs
arcpy.env.overwriteOutput = True
# Set the workspace
arcpy.env.workspace = "C:\Users\LCP"
# Set the extent environment
arcpy.env.extent = "costs.tif"
rowsInPatches_start = arcpy.SearchCursor("selected_patches.shp")
for rowStart in rowsInPatches_start:
ID_patch_start = rowStart.getValue("GRIDCODE")
expressionForSelectInPatches_start = "GRIDCODE=%s" % (ID_patch_start) ## Define SQL expression for the fonction Select Layer By Attribute
# Process: Select Layer By Attribute in Patches_start
arcpy.MakeFeatureLayer_management("selected_patches.shp", "Selected_patch_start", expressionForSelectInPatches_start)
# Process: Cost Distance
outCostDist=CostDistance("Selected_patch_start", "costs.tif", "", "outCostLink.tif")
# Save the output
outCostDist.save("outCostDist.tif")
rowsInSelectedPatches_end = arcpy.SearchCursor("selected_patches.shp")
for rowEnd in rowsInSelectedPatches_end:
ID_patch_end = rowEnd.getValue("GRIDCODE")
expressionForSelectInPatches_end = "GRIDCODE=%s" % (ID_patch_end) ## Define SQL expression for the fonction Select Layer By Attribute
# Process: Select Layer By Attribute in Patches_end
arcpy.MakeFeatureLayer_management("selected_patches.shp", "Selected_patch_end", expressionForSelectInPatches_end)
# Process: Cost Path
outCostPath = CostPath("Selected_patch_end", "outCostDist.tif", "outCostLink.tif", "EACH_ZONE","FID")
# Save the output
outCostPath.save('P_' + str(int(ID_patch_start)) + '_' + str(int(ID_patch_end)) + ".tif")
# Writing in file .txt
outfile=open('P_' + str(int(ID_patch_start)) + '_' + str(int(ID_patch_end)) + ".txt", "w")
rowsTxt = arcpy.SearchCursor('P_' + str(int(ID_patch_start)) + '_' + str(int(ID_patch_end)) + ".tif")
for rowTxt in rowsTxt:
value = rowTxt.getValue("Value")
count = rowTxt.getValue("Count")
pathcost = rowTxt.getValue("PATHCOST")
startrow = rowTxt.getValue("STARTROW")
startcol = rowTxt.getValue("STARTCOL")
print value, count, pathcost, startrow, startcol
outfile.write(str(value) + " " + str(count) + " " + str(pathcost) + " " + str(startrow) + " " + str(startcol) + "\n")
outfile.close()
Thanks very much for your help.
The speed it takes to write to disc vs calculating your cost can be a bottleneck, consider adding a thread to handle all of your writes.
This:
for rowTxt in rowsTxt:
value = rowTxt.getValue("Value")
count = rowTxt.getValue("Count")
pathcost = rowTxt.getValue("PATHCOST")
startrow = rowTxt.getValue("STARTROW")
startcol = rowTxt.getValue("STARTCOL")
print value, count, pathcost, startrow, startcol
outfile.write(str(value) + " " + str(count) + " " + str(pathcost) + " " + str(startrow) + " " + str(startcol) + "\n")
Can be converted into a thread function by making rowsTxt a global variable, and having your thread write to disk from rowsTxt.
After you complete all of your processing you can have an additional global boolean so that your thread function can end when you are done writing everything and you can close your thread.
Example thread function I currently use:
import threading
class ThreadExample:
def __init__(self):
self.receiveThread = None
def startRXThread(self):
self.receiveThread = threading.Thread(target = self.receive)
self.receiveThread.start()
def stopRXThread(self):
if self.receiveThread is not None:
self.receiveThread.__Thread__stop()
self.receiveThread.join()
self.receiveThread = None
def receive(self):
while true:
#do stuff for the life of the thread
#in my case, I listen on a socket for data
#and write it out
So for your case, you could add a class variable to the thread class
self.rowsTxt
and then update your receive to check self.rowsTxt, and if it is not empty, handle it as u do in the code snippet i took from you above. After you handle it, set self.rowsTxt back to None. You could update your threads self.rowsTxt with your main function as it gets rowsTxt. Consider using a buffer like list for self.rowsTxt so you don't miss writing anything.
The most immediate change you can make to significant improve speed would be to switch to data access cursors (e.g. arcpy.da.SearchCursor()). To illustrate, I ran a benchmark test a while back to see the data access cursors perform compared to the old cursors.
The attached figure shows the results of a benchmark test on the new da method UpdateCursor versus the old UpdateCursor method. Essentially, the benchmark test performs the following workflow:
Create random points (10, 100, 1000, 10000, 100000)
Randomly sample from a normal distribution and add value to a new
column in the random points attribute table with a cursor
Run 5 iterations of each random point scenario for both the new and
old UpdateCursor methods and write the mean value to lists
Plot the results
import arcpy, os, numpy, time
arcpy.env.overwriteOutput = True
outws = r'C:\temp'
fc = os.path.join(outws, 'randomPoints.shp')
iterations = [10, 100, 1000, 10000, 100000]
old = []
new = []
meanOld = []
meanNew = []
for x in iterations:
arcpy.CreateRandomPoints_management(outws, 'randomPoints', '', '', x)
arcpy.AddField_management(fc, 'randFloat', 'FLOAT')
for y in range(5):
# Old method ArcGIS 10.0 and earlier
start = time.clock()
rows = arcpy.UpdateCursor(fc)
for row in rows:
# generate random float from normal distribution
s = float(numpy.random.normal(100, 10, 1))
row.randFloat = s
rows.updateRow(row)
del row, rows
end = time.clock()
total = end - start
old.append(total)
del start, end, total
# New method 10.1 and later
start = time.clock()
with arcpy.da.UpdateCursor(fc, ['randFloat']) as cursor:
for row in cursor:
# generate random float from normal distribution
s = float(numpy.random.normal(100, 10, 1))
row[0] = s
cursor.updateRow(row)
end = time.clock()
total = end - start
new.append(total)
del start, end, total
meanOld.append(round(numpy.mean(old),4))
meanNew.append(round(numpy.mean(new),4))
#######################
# plot the results
import matplotlib.pyplot as plt
plt.plot(iterations, meanNew, label = 'New (da)')
plt.plot(iterations, meanOld, label = 'Old')
plt.title('arcpy.da.UpdateCursor -vs- arcpy.UpdateCursor')
plt.xlabel('Random Points')
plt.ylabel('Time (minutes)')
plt.legend(loc = 2)
plt.show()