| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
2 """ Contains implementations ( or improvements ) to mayas geometric shapes """
3 __docformat__ = "restructuredtext"
4
5 import base
6 from mrv.enum import (create as enum, Element as elm)
7 import maya.OpenMaya as api
8 import logging
9 log = logging.getLogger("mrv.maya.nt.geometry")
10
11 __all__ = ("GeometryShape", "DeformableShape", "ControlPoint", "SurfaceShape",
12 "Mesh")
15 """Contains common methods for all geometry types"""
16 @undoable
18 """Copy lightlinks from one meshShape to another
19
20 :param kwargs:
21 * substitute:
22 if True, default False, the other shape will be put
23 in place of self, effectively receiving it's light-links whereas self losses
24 them. This is practical in case you create a new shape below a transform that
25 had a previously visible and manipulated shape whose external connections you
26 wouuld like to keep"""
27 def freeLogicalIndex( parent_plug ):
28 """:return: a free parent compound index"""
29 ilogical = parent_plug.logicalIndex()
30 array_plug = parent_plug.array()
31 num_elments = array_plug.numElements()
32
33
34 # one of the logical indices must be the highest one - start searching
35 # at the end of the physical array
36 for iphysical in xrange( num_elments - 1, -1, -1 ):
37 p_plug = array_plug[ iphysical ]
38 try_index = p_plug.logicalIndex() + 1
39 try_plug = array_plug.elementByLogicalIndex( try_index )
40
41 if try_plug.child( 0 ).minput().isNull():
42 return try_index
43 # END endless loop
44
45 raise AssertionError( "Did not find valid free index" )
46 # END helper method
47
48 substitute = kwargs.get( "substitute", False )
49 for input_plug in self.message.moutputs():
50 node = input_plug.mwrappedNode()
51 if node.apiType() != api.MFn.kLightLink:
52 continue
53
54 # we are always connected to the object portion of the compound model
55 # from there we can conclude it all
56 parent_compound = input_plug.mparent()
57 target_compound_index = -1
58 if substitute:
59 target_compound_index = parent_compound.logicalIndex()
60 else:
61 target_compound_index = freeLogicalIndex(parent_compound)
62 # END get some logical index
63
64 new_parent_compound = parent_compound.array().elementByLogicalIndex( target_compound_index )
65
66 # retrieve light link, connect other - light is only needed if we do not
67 # substitute
68 if not substitute:
69 light_plug = parent_compound.child( 0 ).minput()
70 if not light_plug.isNull():
71 light_plug.mconnectTo(new_parent_compound.child( 0 ), force=False)
72 # END if lightplug is connected
73 # END if no substitute required
74
75 # connect object
76 other.message.mconnectTo(new_parent_compound.child(1))
77
78
79 # END for each output plug
80
81
82 -class DeformableShape( GeometryShape ): # base for epydoc !
83 pass
84
87 pass
88
91 pass
92
97 """Utility producing components, initialized with the given indices. See `Mesh`
98 for more info. """
99 __slots__ = ('_mesh', '_component')
100 # to detect slices, funny thing to remark: Maya passes 1 << 31 - 1 for some reason
101 # we want to be smaller, hence -2
102 _int32b = ( 1 << 31 ) - 2
103
107
109 comp = self._mesh.component(self._component)
110 # for some reason , python inside maya returns 31 bit ints to indicate
111 # slices, instead of sys.maxint. To be sure we handle all, we just
112 # check larger/than cases
113 # handle [:] slices
114 if j > self._int32b:
115 comp.setComplete(1)
116 else:
117 comp.addElements(api.MIntArray.mfromRange(i, j))
118 # END handle slice range
119 return comp
120
122 comp = self._mesh.component(self._component)
123 ia = None
124 if len(args) == 1:
125 arg = args[0]
126 if hasattr(arg, 'next'):
127 ia = api.MIntArray.mfromIter(arg)
128 elif isinstance(arg, (list, tuple)):
129 ia = api.MIntArray.mfromList(arg)
130 elif isinstance(arg, api.MIntArray):
131 ia = arg
132 else:
133 ia = api.MIntArray.mfromMultiple(arg)
134 # END handle type
135 else:
136 ia = api.MIntArray.mfromList(args)
137 # END handle args
138
139 return comp.addElements(ia)
140
144
147 """Utility which produces iterators for the component type
148 it was initialized with. As a bonus, it allows to return
149 quick constrained iterators using the slice and get-item notation"""
150 __slots__ = tuple()
151
154
156 return self._mesh.iterComponents(self._component)
157
159 """
160 :raise NotImplementedError: if comp needs double-index component, our interface
161 cannot support anything else than SingleIndex components"""
162 if self._component == Mesh.eComponentType.uv:
163 raise NotImplementedError("This Utility does not support iteration using \
164 component-constrained iterators as it can only reproduce \
165 SingleIndexedComponents - create the Component yourself and \
166 use iterComponents to retrieve the iterator instead")
167
169 self._check_component()
170 # skip full slices, as in fact no components are needed there.
171 if j > self._int32b:
172 return self._get_complete_iterator()
173 # END handle [:] slice
174
175 comp = super(_SingleIndexedComponentIterator, self).__getslice__(i,j)
176 return self._mesh.iterComponents(self._component, comp)
177
179 self._check_component()
180 comp = super(_SingleIndexedComponentIterator, self).__getitem__(*args)
181 return self._mesh.iterComponents(self._component, comp)
182
183
187
188 # shortcut alias
189 iter = property(iterator)
190
195 """Implemnetation of mesh related methods to make its handling more
196 convenient
197
198 **Component Access**:
199
200 >>> m.cvtx[:] # a complete set of components
201 >>> m.cvtx[1:4] # initialized with 3 indices
202 >>> m.cvtx[1] # initialized with a single index
203 >>> m.cvtx[1,2,3] # initialized with multiple indices
204 >>> m.cf[(1,2,3)] # initialized with list or tuple
205 >>> m.ce[iter(1,2,3)] # initialized from iterator
206 >>> m.ce[api.MIntArray()] # initialized from MIntArray
207
208 """
209 # component types that make up a mesh
210 eComponentType = enum( elm("vertex", api.MFn.kMeshVertComponent),
211 elm("edge", api.MFn.kMeshEdgeComponent ),
212 elm("face", api.MFn.kMeshPolygonComponent ),
213 elm("uv", api.MFn.kMeshMapComponent ) )
214
215 #{ Iterator Shortcuts
219 # END internal method
220 return internal
221 # END pseudo-decorator
222
223 # SETUP ITERATOR SHORTCUTS
224 for shortname, component in zip(('vtx', 'e', 'f', 'map'), eComponentType):
225 locals()[shortname] = property(_make_component_getter(_SingleIndexedComponentIterator, component))
226
227 # SETUP COMPONENT SHORTCUTS
228 for shortname, component in zip(('cvtx', 'ce', 'cf', 'cmap'), eComponentType):
229 locals()[shortname] = property(_make_component_getter(_SingleIndexedComponentGenerator, component))
230
231 #} END iterator shortcuts
232
233 #{ Utilities
234
236 """Copy our tweaks onto another mesh
237
238 :note: we do not check topology for maximum flexibility"""
239 opnts = other.pnts
240 pnts = self.pnts
241 for splug in pnts:
242 opnts.elementByLogicalIndex( splug.logicalIndex() ).msetMObject( splug.asMObject() )
243 # END for each source plug in pnts
244
246 """
247 :return: True if we are nonempty and valid - emptry meshes do not work with the mfnmesh
248 although it should ! Have to catch that case ourselves"""
249 try:
250 self.numVertices()
251 return True
252 except RuntimeError:
253 return False
254
255 @undoable
257 """Copy set assignments including component assignments to other
258
259 :param kwargs: passed to set.addMember, additional kwargs are:
260 * setFilter: default is fSetsRenderable"""
261 setFilter = kwargs.pop( "setFilter", base.Shape.fSetsRenderable )
262 for sg, comp in self.componentAssignments( setFilter = setFilter ):
263 sg.addMember( other, comp, **kwargs )
264
265
266 @undoable
268 """Reset the tweaks on the given mesh shape
269
270 :param tweak_type: the component type(s) whose tweaks are to be removed,
271 valid values are 'vertex' and 'uv' members of the eComponentType enumeration.
272 Pass in a scalar value or a list of tweak types
273 :param keep_tweak_result: if True, the effect of the tweak will be kept. If False,
274 it will be removed. What actually happens depends on the context
275
276 * [referenced] mesh *without* history:
277 copy outMesh to inMesh, resetTweaks
278
279 if referenced, plenty of reference edits are generated, ideally one operates
280 on non-referenced geomtry
281
282 * [referenced] mesh *with* history:
283 put tweakNode into mesh history, copy tweaks onto tweak node
284 :note: currently vertex and uv tweaks will be removed if keep is enabled, thus they must
285 both be specified"""
286 check_types = ( isinstance( tweak_type, ( list, tuple ) ) and tweak_type ) or [ tweak_type ]
287 type_map = {
288 self.eComponentType.vertex : ( "pnts", api.MFnNumericData.k3Float, "polyTweak", api.MFn.kPolyTweak, "tweak" ),
289 self.eComponentType.uv : ( "uvpt", api.MFnNumericData.k2Float, "polyTweakUV", api.MFn.kPolyTweakUV, "uvTweak" )
290 }
291
292 mia = api.MIntArray()
293 for reset_this_type in check_types:
294 try:
295 attrname, datatype, tweak_node_type, tweak_node_type_API, tweakattr = type_map[ reset_this_type ]
296 except KeyError:
297 raise ValueError( "Tweak type %s is not supported" % reset_this_type )
298
299 # KEEP MODE
300 #############
301 if keep_tweak_result:
302 input_plug = self.inMesh.minput()
303
304 # history check
305 if input_plug.isNull():
306 # assert as we had to make the handling much more complex to allow this to work right as we copy the whole mesh here
307 # containing all tweaks , not only one type
308 if not ( self.eComponentType.vertex in check_types and self.eComponentType.uv in check_types ):
309 log.warn("Currently vertex AND uv tweaks will be removed if a mesh has no history and a reset is requested")
310 # END print warning
311
312 # take the output mesh, and stuff it into the input, then proceed
313 # with the reset. This implies that all tweaks have to be removed
314 out_mesh = self.outMesh.asMObject()
315 self.inMesh.msetMObject( out_mesh )
316 self.cachedInMesh.msetMObject( out_mesh )
317
318 # finally reset all tweeaks
319 return self.resetTweaks( check_types, keep_tweak_result = False )
320 else:
321 # create node of valid type
322 tweak_node = input_plug.mwrappedNode()
323
324 # create node if there is none as direct input
325 if not tweak_node.hasFn( tweak_node_type_API ):
326 tweak_node = base.createNode( "polyTweak", tweak_node_type, forceNewLeaf = 1 )
327
328 # hook the node into the history
329 input_plug.mconnectTo(tweak_node.inputPolymesh)
330 tweak_node.output.mconnectTo(self.inMesh)
331
332 # setup uvset tweak location to tell uvset where to get tweaks from
333 if tweak_node_type_API == api.MFn.kPolyTweakUV:
334 names = list()
335 self.getUVSetNames( names )
336 index = names.index( self.currentUVSetName( ) )
337
338 own_tweak_location_plug = self.uvSet.elementByLogicalIndex( index ).mchildByName('uvSetTweakLocation')
339 tweak_node.uvTweak.elementByLogicalIndex( index ).mconnectTo(own_tweak_location_plug)
340 # END uv special setup
341 # END create tweak node
342
343 dtweak_plug = tweak_node.findPlug(tweakattr)
344 stweak_plug = self.findPlug(attrname)
345
346 # copy the tweak values - iterate manually as the plug tends to
347 # report incorrect values if history is present - its odd
348 stweak_plug.evaluateNumElements()
349
350 mia.clear()
351 stweak_plug.getExistingArrayAttributeIndices(mia)
352 for i in mia:
353 try:
354 tplug = stweak_plug.elementByLogicalIndex(i)
355 except RuntimeError:
356 continue
357 else:
358 dtweak_plug.elementByLogicalIndex(i).msetMObject(tplug.asMObject())
359 # END exception handling
360 # END for each tweak plug
361
362 # proceed with reset of tweaks
363 pass
364 # END history handling
365 # END keep tweak result handling
366
367 arrayplug = self.findPlug(attrname)
368 dataobj = api.MFnNumericData().create( datatype )
369
370 # reset values, do it for all components at once using a data object
371 try:
372 for p in arrayplug:
373 p.msetMObject( dataobj )
374 except RuntimeError:
375 # especially uvtweak array plugs return incorrect lengths, thus we may
376 # fail once we reach the end of the iteration.
377 # uvpt appears to display a lenght equalling the number of uvpoints in the mesh
378 # possibly only for the current uvset
379 pass
380 # END for tweak type to reset
381
383 """:return: A component object able to hold the given component type
384 :param component_type: a member of the `eComponentType` enumeration"""
385 if component_type not in self.eComponentType:
386 raise ValueError("Invalid component type")
387 return base.SingleIndexedComponent.create(component_type.value())
388 # END handle face-vertex components
389
390 #} END utilities
391
392 #{ Iterators
394 """
395 :return: MItIterator matching your component_type to iteartor over items
396 on this mesh
397 :param component_type:
398 * vertex -> MItMeshVertex
399 * edge -> MItMeshEdge
400 * face -> MItMeshPolygon
401 * uv -> MItMeshFaceVertex
402 :param component: if not kNullObject, the iterator returned will be constrained
403 to the given indices as described by the Component. Use `component` to retrieve
404 a matching component type's instance"""
405 if component_type not in self.eComponentType:
406 raise ValueError("Invalid component type")
407
408 ec = self.eComponentType
409 it_type = { ec.vertex : api.MItMeshVertex,
410 ec.edge : api.MItMeshEdge,
411 ec.face : api.MItMeshPolygon,
412 ec.uv : api.MItMeshFaceVertex}[component_type]
413
414 return it_type(self.dagPath(), component)
415
416 #} END iterators
417
418 #( iDuplicatable
420 """Copy tweaks and sets from other onto self
421
422 :param kwargs:
423 * setFilter: if given, default is fSets, you may specify the types of sets to copy
424 if None, no set conenctions will be copied """
425 other.copyTweaksTo( self )
426
427 setfilter = kwargs.pop( "setFilter", Mesh.fSets ) # copy all sets by default
428 if setfilter is not None:
429 other.copyAssignmentsTo( self, setFilter = setfilter )
430
431 #) END iDuplicatable
432
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 19 18:00:25 2011 | http://epydoc.sourceforge.net |