1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_USD_USD_GEOM_XFORM_OP_H
25 #define PXR_USD_USD_GEOM_XFORM_OP_H
26 
27 /// \file usdGeom/xformOp.h
28 
29 #include "pxr/pxr.h"
30 #include "pxr/usd/usdGeom/api.h"
31 #include "pxr/usd/usd/attribute.h"
32 #include "pxr/usd/usd/attributeQuery.h"
33 #include "pxr/usd/usdGeom/tokens.h"
34 
35 #include <string>
36 #include <vector>
37 #include <typeinfo>
38 
39 #include <boost/variant.hpp>
40 
41 #include "pxr/base/tf/staticTokens.h"
42 
43 PXR_NAMESPACE_OPEN_SCOPE
44 
45 
46 /// \hideinitializer
47 #define USDGEOM_XFORM_OP_TYPES \
48     (translate) \
49     (scale) \
50     (rotateX) \
51     (rotateY) \
52     (rotateZ) \
53     (rotateXYZ) \
54     (rotateXZY) \
55     (rotateYXZ) \
56     (rotateYZX) \
57     (rotateZXY) \
58     (rotateZYX) \
59     (orient) \
60     (transform) \
61     ((resetXformStack, "!resetXformStack!"))
62 
63 /// \anchor UsdGeomXformOpTypes
64 /// Provides TfToken's for use in conjunction with UsdGeomXformable::Add
65 /// XformOp() and UsdGeomXformOp::GetOpType(), to establish op type.
66 ///
67 /// The component operation names and their meanings are:
68 /// \li <b>translate</b> - XYZ translation
69 /// \li <b>scale</b> - XYZ scale
70 /// \li <b>rotateX</b> - rotation about the X axis, <b>in degrees</b>
71 /// \li <b>rotateY</b> - rotation about the Y axis, <b>in degrees</b>
72 /// \li <b>rotateZ</b> - rotation about the Z axis, <b>in degrees</b>
73 /// \li <b>rotateXYZ, rotateXZY, rotateYXZ, rotateYZX, rotateZXY, rotateZYX</b>
74 ///     - a set of three canonical Euler rotations, packed into a single
75 ///     Vec3, for conciseness and efficiency of reading.  The \\em first
76 ///     axis named is the most local, so a single rotateXYZ is equivalent to
77 ///     the ordered ops "rotateZ rotateY rotateX". See also
78 ///     \ref usdGeom_rotationPackingOrder "note on rotation packing order."
79 /// \li <b>orient</b>  - arbitrary axis/angle rotation, expressed as a quaternion
80 /// \li <b>transform</b> - 4x4 matrix transformation
81 /// \li <b>resetXformStack</b> - when appearing as the first op, instructs
82 ///     client that the transform stack should be cleared of any inherited
83 ///     transformation prior to processing the rest of the prims ops.  It is
84 ///     an error for resetXformStack to appear anywhere other than as the
85 ///     first element in \em xformOpOrder.
86 TF_DECLARE_PUBLIC_TOKENS(UsdGeomXformOpTypes, USDGEOM_API, USDGEOM_XFORM_OP_TYPES);
87 
88 /// \class UsdGeomXformOp
89 ///
90 /// Schema wrapper for UsdAttribute for authoring and computing
91 /// transformation operations, as consumed by UsdGeomXformable schema.
92 ///
93 /// The semantics of an op are determined primarily by its name, which allows
94 /// us to decode an op very efficiently.  All ops are independent attributes,
95 /// which must live in the "xformOp" property namespace.  The op's primary name
96 /// within the namespace must be one of \ref UsdGeomXformOpTypes, which
97 /// determines the type of transformation operation, and its secondary name
98 /// (or suffix) within the namespace (which is not required to exist), can be
99 /// any name that distinguishes it from other ops of the same type. Suffixes
100 /// are generally imposed by higer level xform API schemas.
101 ///
102 /// \anchor usdGeom_rotationPackingOrder
103 /// \note
104 /// <b>On packing order of rotateABC triples</b><br>
105 /// The order in which the axis rotations are recorded in a Vec3* for the
106 /// six \em rotateABC Euler triples <b>is always the same:</b> vec[0] = X,
107 /// vec[1] = Y, vec[2] = Z .  The \em A, \em B, \em C in the op name dictate
108 /// the order in which their corresponding elements are consumed by the
109 /// rotation, not how they are laid out.
110 ///
111 class UsdGeomXformOp
112 {
113 public:
114 
115     /// Enumerates the set of all transformation operation types.
116     enum Type {
117         TypeInvalid,   ///< Represents an invalid xformOp.
118         TypeTranslate, ///< XYZ translation.
119         TypeScale,     ///< XYZ scale.
120         TypeRotateX,   ///< Rotation about the X-axis, <b>in degrees</b>.
121         TypeRotateY,   ///< Rotation about the Y-axis, <b>in degrees</b>.
122         TypeRotateZ,   ///< Rotation about the Z-axis, <b>in degrees</b>.
123         TypeRotateXYZ, ///< Set of 3 canonical Euler rotations
124                        ///  \ref usdGeom_rotationPackingOrder "in XYZ order"
125         TypeRotateXZY, ///< Set of 3 canonical Euler rotations
126                        /// \ref usdGeom_rotationPackingOrder "in XZY order"
127         TypeRotateYXZ, ///< Set of 3 canonical Euler rotations
128                        /// \ref usdGeom_rotationPackingOrder "in YXZ order"
129         TypeRotateYZX, ///< Set of 3 canonical Euler rotations
130                        /// \ref usdGeom_rotationPackingOrder "in YZX order"
131         TypeRotateZXY, ///< Set of 3 canonical Euler rotations
132                        /// \ref usdGeom_rotationPackingOrder "in ZXY order"
133         TypeRotateZYX, ///< Set of 3 canonical Euler rotations
134                        /// \ref usdGeom_rotationPackingOrder "in ZYX order"
135         TypeOrient,    ///< Arbitrary axis/angle rotation, expressed as a quaternion.
136         TypeTransform  ///< A 4x4 matrix transformation.
137     };
138 
139     /// Precision with which the value of the tranformation operation is encoded.
140     enum Precision {
141         PrecisionDouble, ///< Double precision
142         PrecisionFloat,  ///< Floating-point precision
143         PrecisionHalf    ///< Half-float precision
144     };
145 
146     // Default constructor returns an invalid XformOp.  Exists for
147     // container classes
UsdGeomXformOp()148     UsdGeomXformOp()
149     {
150         /* NOTHING */
151     }
152 
153     /// Speculative constructor that will produce a valid UsdGeomXformOp when
154     /// \p attr already represents an attribute that is XformOp, and
155     /// produces an \em invalid XformOp otherwise (i.e.
156     /// explicit-bool conversion operator will return false).
157     ///
158     /// Calling \c UsdGeomXformOp::IsXformOp(attr) will return the same truth
159     /// value as this constructor, but if you plan to subsequently use the
160     /// XformOp anyways, just use this constructor.
161     ///
162     /// \p isInverseOp is set to true to indicate an inverse transformation
163     /// op.
164     ///
165     /// This constructor exists mainly for internal use. Clients should use
166     /// AddXformOp API (or one of Add*Op convenience API) to create and retain
167     /// a copy of an UsdGeomXformOp object.
168     ///
169     USDGEOM_API
170     explicit UsdGeomXformOp(const UsdAttribute &attr, bool isInverseOp=false);
171 
172     // -------------------------------------------------------
173     /// \name Static Helper API
174     // -------------------------------------------------------
175 
176     /// Test whether a given UsdAttribute represents valid XformOp, which
177     /// implies that creating a UsdGeomXformOp from the attribute will succeed.
178     ///
179     /// Success implies that \c attr.IsDefined() is true.
180     USDGEOM_API
181     static bool IsXformOp(const UsdAttribute &attr);
182 
183     /// Test whether a given attrbute name represents a valid XformOp, which
184     /// implies that creating a UsdGeomXformOp from the corresponding
185     /// UsdAttribute will succeed.
186     ///
187     USDGEOM_API
188     static bool IsXformOp(const TfToken &attrName);
189 
190     /// Returns the TfToken used to encode the given \p opType.
191     /// Note that an empty TfToken is used to represent TypeInvalid
192     USDGEOM_API
193     static TfToken const &GetOpTypeToken(Type const opType);
194 
195     /// Returns the Type enum associated with the given \p opTypeToken.
196     USDGEOM_API
197     static Type GetOpTypeEnum(TfToken const &opTypeToken);
198 
199     /// Returns the precision corresponding to the given value typeName.
200     USDGEOM_API
201     static Precision GetPrecisionFromValueTypeName(const SdfValueTypeName& typeName);
202 
203     /// Returns the value typeName token that corresponds to the given
204     /// combination of \p opType and \p precision.
205     USDGEOM_API
206     static const SdfValueTypeName &GetValueTypeName(const Type opType,
207                                                     const Precision precision);
208 
209     /// Returns the xformOp's name as it appears in xformOpOrder, given
210     /// the opType, the (optional) suffix and whether it is an inverse
211     /// operation.
212     USDGEOM_API
213     static TfToken GetOpName(const Type opType,
214                              const TfToken &opSuffix=TfToken(),
215                              bool inverse=false);
216 
217     // -------------------------------------------------------
218     /// \name Data Encoding Queries
219     // -------------------------------------------------------
220 
221     /// Return the operation type of this op, one of \ref UsdGeomXformOp::Type
GetOpType()222     Type GetOpType() const {
223         return _opType;
224     }
225 
226     /// Returns the precision level of the xform op.
227     USDGEOM_API
228     Precision GetPrecision() const;
229 
230     /// Returns whether the xformOp represents an inverse operation.
IsInverseOp()231     bool IsInverseOp() const {
232         return _isInverseOp;
233     }
234 
235     /// Returns the opName as it appears in the xformOpOrder attribute.
236     ///
237     /// This will begin with "!invert!:xformOp:" if it is an inverse xform
238     /// operation. If it is not an inverse xformOp, it will begin with 'xformOp:'.
239     ///
240     /// This will be empty for an invalid xformOp.
241     ///
242     USDGEOM_API
243     TfToken GetOpName() const;
244 
245     /// Does this op have the given suffix in its name.
246     USDGEOM_API
247     bool HasSuffix(TfToken const &suffix) const;
248 
249     // ---------------------------------------------------------------
250     /// \name Computing with Ops
251     // ---------------------------------------------------------------
252 
253     /// We allow ops to be encoded with varying degrees of precision,
254     /// depending on the clients needs and constraints.  GetAs() will
255     /// attempt to convert the stored data to the requested datatype.
256     ///
257     /// Note this accessor incurs some overhead beyond Get()'ing the
258     /// value as a VtValue and dealing with the results yourself.
259     ///
260     /// \return true if a value was successfully read \em and converted
261     /// to the requested datatype (see \ref VtValue::Cast()), false
262     /// otherwise.  A problem reading or failure to convert will cause
263     /// an error to be emitted.
264     ///
265     /// \note the requested type \p T must be constructable by assignment
266     template <typename T>
GetAs(T * value,UsdTimeCode time)267     bool GetAs(T* value, UsdTimeCode time) const {
268         VtValue v;
269         if (!Get(&v, time)) {
270             return false;
271         }
272         v.Cast<T>();
273         if (v.IsEmpty()){
274             TfType thisType = GetTypeName().GetType();
275             TF_CODING_ERROR("Unable to convert xformOp %s's value from %s to "
276                             "requested type %s.", GetAttr().GetPath().GetText(),
277                             thisType.GetTypeName().c_str(),
278                             TfType::GetCanonicalTypeName(typeid(*value)).c_str());
279             return false;
280         }
281         *value = v.UncheckedGet<T>();
282         return true;
283     }
284 
285     /// Return the 4x4 matrix that applies the transformation encoded
286     /// by op \p opType and data value \p opVal.
287     ///
288     /// If \p isInverseOp is true, then the inverse of the tranformation
289     /// represented by the op/value pair is returned.
290     ///
291     /// An error will be issued if \p opType is not one of the values in the enum
292     /// \ref UsdGeomXformOp::Type or if \p opVal cannot be converted
293     /// to a suitable input to \p opType
294     USDGEOM_API
295     static GfMatrix4d GetOpTransform(Type const opType,
296                                      VtValue const &opVal,
297                                      bool isInverseOp=false);
298 
299 
300     /// Return the 4x4 matrix that applies the transformation encoded
301     /// in this op at \p time.
302     ///
303     /// Returns the identity matrix and issues a coding error if the op is
304     /// invalid.
305     ///
306     /// If the op is valid, but has no authored value, the identity
307     /// matrix is returned and no error is issued.
308     ///
309     USDGEOM_API
310     GfMatrix4d GetOpTransform(UsdTimeCode time) const;
311 
312     /// Determine whether there is any possibility that this op's value
313     /// may vary over time.
314     ///
315     /// The determination is based on a snapshot of the authored state of the
316     /// op, and may become invalid in the face of further authoring.
MightBeTimeVarying()317     bool MightBeTimeVarying() const {
318         return boost::apply_visitor(_GetMightBeTimeVarying(), _attr);
319     }
320 
321     // ---------------------------------------------------------------
322     /// \name UsdAttribute API
323     // ---------------------------------------------------------------
324 
325     /// Allow UsdGeomXformOp to auto-convert to UsdAttribute, so you can
326     /// pass a UsdGeomXformOp to any function that accepts a UsdAttribute or
327     /// const-ref thereto.
328     operator UsdAttribute const& () const { return GetAttr(); }
329 
330     /// Explicit UsdAttribute extractor
GetAttr()331     UsdAttribute const &GetAttr() const {
332         return boost::apply_visitor(_GetAttr(), _attr);
333     }
334 
335     /// Return true if the wrapped UsdAttribute::IsDefined(), and in
336     /// addition the attribute is identified as a XformOp.
IsDefined()337     bool IsDefined() const { return IsXformOp(GetAttr()); }
338 
339 public:
340     /// \anchor UsdGeomXformOp_explicit_bool
341     /// Explicit bool conversion operator. An XformOp object converts to
342     /// \c true iff it is valid for querying and authoring values and metadata,
343     /// (which is identically equivalent to IsDefined()), and converts to
344     /// \c false otherwise.
345     explicit operator bool() const {
346         return IsDefined();
347     }
348 
349     /// Equality comparison.  Return true if \a lhs and \a rhs represent the
350     /// same underlying UsdAttribute, false otherwise.
351     friend bool operator==(const UsdGeomXformOp &lhs,
352                            const UsdGeomXformOp &rhs) {
353         return lhs.GetAttr() == rhs.GetAttr();
354     }
355 
356     /// Inequality comparison. Return false if \a lhs and \a rhs represent the
357     /// same underlying UsdAttribute, true otherwise.
358     friend bool operator!=(const UsdGeomXformOp &lhs,
359                            const UsdGeomXformOp &rhs) {
360         return !(lhs == rhs);
361     }
362 
363     /// \sa UsdAttribute::GetName()
GetName()364     TfToken const &GetName() const {  return GetAttr().GetName(); }
365 
366     /// \sa UsdAttribute::GetBaseName()
GetBaseName()367     TfToken GetBaseName() const { return GetAttr().GetBaseName(); }
368 
369     /// \sa UsdAttribute::GetNamespace()
GetNamespace()370     TfToken GetNamespace() const { return GetAttr().GetNamespace(); }
371 
372     /// \sa UsdAttribute::SplitName()
SplitName()373     std::vector<std::string> SplitName() const { return GetAttr().SplitName(); };
374 
375     /// \sa UsdAttribute::GetTypeName()
GetTypeName()376     SdfValueTypeName GetTypeName() const { return GetAttr().GetTypeName(); }
377 
378     /// Get the attribute value of the XformOp at \p time.
379     ///
380     /// \note For inverted ops, this returns the raw, uninverted value.
381     ///
382     template <typename T>
383     bool Get(T* value, UsdTimeCode time = UsdTimeCode::Default()) const {
384         return boost::apply_visitor(_Get<T>(value, time), _attr);
385     }
386 
387     /// Set the attribute value of the XformOp at \p time
388     ///
389     /// \note This only works on non-inverse operations. If invoked on
390     /// an inverse xform operation, a coding error is issued and no value is
391     /// authored.
392     ///
393     template <typename T>
394     bool Set(T const & value, UsdTimeCode time = UsdTimeCode::Default()) const {
395         // Issue a coding error and return without setting value,
396         // if this is an inverse op.
397         if (_isInverseOp) {
398             TF_CODING_ERROR("Cannot set a value on the inverse xformOp '%s'. "
399                 "Please set value on the paired non-inverse xformOp instead.",
400                 GetOpName().GetText());
401             return false;
402         }
403 
404         return GetAttr().Set(value, time);
405     }
406 
407     /// Populates the list of time samples at which the associated attribute
408     /// is authored.
GetTimeSamples(std::vector<double> * times)409     bool GetTimeSamples(std::vector<double> *times) const {
410         return boost::apply_visitor(_GetTimeSamples(times), _attr);
411     }
412 
413     /// Populates the list of time samples within the given \p interval,
414     /// at which the associated attribute is authored.
GetTimeSamplesInInterval(const GfInterval & interval,std::vector<double> * times)415     bool GetTimeSamplesInInterval(const GfInterval &interval,
416                                   std::vector<double> *times) const {
417         return boost::apply_visitor(
418                 _GetTimeSamplesInInterval(interval, times), _attr);
419     }
420 
421     /// Returns the number of time samples authored for this xformOp.
GetNumTimeSamples()422     size_t GetNumTimeSamples() const {
423         return boost::apply_visitor(_GetNumTimeSamples(), _attr);
424     }
425 
426 private:
427     struct _ValidAttributeTagType {};
428 
429 public:
430     // Allow clients that guarantee \p attr is valid avoid having
431     // UsdGeomXformOp's ctor check again.
432     USDGEOM_API
433     UsdGeomXformOp(const UsdAttribute &attr, bool isInverseOp,
434                    _ValidAttributeTagType);
435     USDGEOM_API
436     UsdGeomXformOp(UsdAttributeQuery &&query, bool isInverseOp,
437                    _ValidAttributeTagType);
438 private:
439     friend class UsdGeomXformable;
440 
441     // Shared initialization function.
442     void _Init();
443 
444     // Return the op-type for the string value \p str.
445     static Type _GetOpTypeEnumFromCString(char const *str, size_t len);
446 
447     // Returns the attribute belonging to \p prim that corresponds to the
448     // given \p opName. It also populates the output parameter \p isInverseOp
449     // appropriately.
450     //
451     // The attribute that's returned will be invalid if the
452     // corresponding xformOp attribute doesn't exist on the prim.
453     //
454     static UsdAttribute _GetXformOpAttr(UsdPrim const& prim,
455         const TfToken &opName, bool *isInverseOp);
456 
457     // Private method for creating and using an attribute query interally for
458     // this xformOp.
_CreateAttributeQuery()459     void _CreateAttributeQuery() const {
460         _attr = UsdAttributeQuery(GetAttr());
461     }
462 
463     // Factory for UsdGeomXformable's use, so that we can encapsulate the
464     // logic of what discriminates XformOp in this class, while
465     // preserving the pattern that attributes can only be created
466     // via their container objects.
467     //
468     // \p opType must be one of UsdGeomXformOp::Type
469     //
470     // \p precision must be one of UsdGeomXformOp::Precision.
471     //
472     // \return an invalid UsdGeomXformOp if we failed to create a valid
473     // attribute, a valid UsdGeomXformOp otherwise.  It is not an
474     // error to create over an existing, compatible attribute.
475     //
476     // It is a failed verification for \p prim to be invalid/expired
477     //
478     // \sa UsdPrim::CreateAttribute()
479     UsdGeomXformOp(UsdPrim const& prim, Type const opType,
480                    Precision const precision, TfToken const &opSuffix=TfToken(),
481                    bool inverse=false);
482 
483     // UsdAttributeQuery already contains a copy of the associated UsdAttribute.
484     // To minimize the memory usage, we only store one or the other.
485     //
486     // The lifetime of a UsdAttributeQuery needs to be managed very carefully as
487     // it gets invalidated whenever the associated attribute is authored.
488     // Hence, access to the creation of an attribute query is restricted inside
489     // a private member function named _CreateAttributeQuery().
490     //
491     mutable boost::variant<UsdAttribute, UsdAttributeQuery> _attr;
492 
493     Type _opType;
494     bool _isInverseOp;
495 
496     // Visitor for getting xformOp value.
497     template <class T>
498     struct _Get : public boost::static_visitor<bool>
499     {
500         _Get(T *value_,
value_Get501              UsdTimeCode time_ = UsdTimeCode::Default()) : value (value_), time(time_)
502         {}
503 
operator_Get504         bool operator()(const UsdAttribute &attr) const
505         {
506             return attr.Get(value, time);
507         }
508 
operator_Get509         bool operator()(const UsdAttributeQuery &attrQuery) const
510         {
511             return attrQuery.Get(value, time);
512         }
513 
514         T *value;
515         UsdTimeCode time;
516     };
517 
518     // Visitor for getting a const-reference to the UsdAttribute.
519     struct _GetAttr : public boost::static_visitor<const UsdAttribute &> {
520 
_GetAttr_GetAttr521         _GetAttr() {}
522 
operator_GetAttr523         const UsdAttribute &operator()(const UsdAttribute &attr) const
524         {
525             return attr;
526         }
527 
operator_GetAttr528         const UsdAttribute &operator()(const UsdAttributeQuery &attrQuery) const
529         {
530             return attrQuery.GetAttribute();
531         }
532     };
533 
534     // Visitor for getting all the time samples.
535     struct _GetTimeSamples : public boost::static_visitor<bool> {
536 
_GetTimeSamples_GetTimeSamples537         _GetTimeSamples(std::vector<double> *times_) : times(times_) {}
538 
operator_GetTimeSamples539         bool operator()(const UsdAttribute &attr) const
540         {
541             return attr.GetTimeSamples(times);
542         }
543 
operator_GetTimeSamples544         bool operator()(const UsdAttributeQuery &attrQuery) const
545         {
546             return attrQuery.GetTimeSamples(times);
547         }
548 
549         std::vector<double> *times;
550     };
551 
552     // Visitor for getting all the time samples within a given interval.
553     struct _GetTimeSamplesInInterval : public boost::static_visitor<bool> {
554 
_GetTimeSamplesInInterval_GetTimeSamplesInInterval555         _GetTimeSamplesInInterval(const GfInterval &interval_,
556                                   std::vector<double> *times_)
557             : interval(interval_), times(times_)
558         {}
559 
operator_GetTimeSamplesInInterval560         bool operator()(const UsdAttribute &attr) const
561         {
562             return attr.GetTimeSamplesInInterval(interval, times);
563         }
564 
operator_GetTimeSamplesInInterval565         bool operator()(const UsdAttributeQuery &attrQuery) const
566         {
567             return attrQuery.GetTimeSamplesInInterval(interval, times);
568         }
569 
570         const GfInterval &interval;
571         std::vector<double> *times;
572     };
573 
574     // Visitor for getting the number of time samples.
575     struct _GetNumTimeSamples : public boost::static_visitor<size_t> {
576 
_GetNumTimeSamples_GetNumTimeSamples577         _GetNumTimeSamples() {}
578 
operator_GetNumTimeSamples579         size_t operator()(const UsdAttribute &attr) const
580         {
581             return attr.GetNumTimeSamples();
582         }
583 
operator_GetNumTimeSamples584         size_t operator()(const UsdAttributeQuery &attrQuery) const
585         {
586             return attrQuery.GetNumTimeSamples();
587         }
588     };
589 
590     // Visitor for determining whether the op might vary over time.
591     struct _GetMightBeTimeVarying : public boost::static_visitor<bool> {
592 
_GetMightBeTimeVarying_GetMightBeTimeVarying593         _GetMightBeTimeVarying() {}
594 
operator_GetMightBeTimeVarying595         bool operator()(const UsdAttribute &attr) const
596         {
597             return attr.ValueMightBeTimeVarying();
598         }
599 
operator_GetMightBeTimeVarying600         bool operator()(const UsdAttributeQuery &attrQuery) const
601         {
602             return attrQuery.ValueMightBeTimeVarying();
603         }
604     };
605 
606 };
607 
608 
609 
610 PXR_NAMESPACE_CLOSE_SCOPE
611 
612 #endif // USD_XFORMOP_H
613