HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SIM_ConstraintNetworkIterator.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  */
6 
7 #ifndef __SIM_ConstraintNetworkIterator_h__
8 #define __SIM_ConstraintNetworkIterator_h__
9 
10 #include "SIM_API.h"
11 
12 #include "SIM_DataFilter.h"
13 #include "SIM_OptionsUser.h"
14 
15 #include <GA/GA_AttributeRef.h>
16 #include <GA/GA_Handle.h>
17 #include <GU/GU_DetailHandle.h>
18 #include <UT/UT_NonCopyable.h>
19 #include <UT/UT_StringMap.h>
20 #include <UT/UT_VectorTypes.h>
21 
22 class GA_Primitive;
23 class GA_PrimitiveGroup;
24 class GU_Detail;
26 class SIM_Data;
27 class SIM_Geometry;
29 class SIM_GeometryCopy;
30 class SIM_Object;
31 class SIM_Time;
32 
33 namespace SIM_ConstraintNetwork
34 {
35  /// Standard attribute names.
36  namespace Names
37  {
42 
43  extern SIM_API const UT_StringHolder position;
44  extern SIM_API const UT_StringHolder rotation;
45  extern SIM_API const UT_StringHolder all;
46 
47  extern SIM_API const UT_StringHolder force;
48  extern SIM_API const UT_StringHolder distance;
49  extern SIM_API const UT_StringHolder torque;
50  extern SIM_API const UT_StringHolder angle;
51 
52  extern SIM_API const UT_StringHolder vertex;
53  extern SIM_API const UT_StringHolder agent;
54  extern SIM_API const UT_StringHolder local_P;
56  extern SIM_API const UT_StringHolder anchor_id;
57  extern SIM_API const UT_StringHolder anchor_pid;
58  extern SIM_API const UT_StringHolder anchor_vid;
59  extern SIM_API const UT_StringHolder anchor_type;
60 
61  extern SIM_API const UT_StringHolder broken;
62  }
63 
64  /// The type of a constraint (linear, angular, or both).
65  enum class ConstraintType
66  {
67  Position,
68  Rotation,
69  All
70  };
71 
72  /// The type of anchor specfied by 'anchor_type' attribute. Defaults to
73  /// point.
74  enum class AnchorType
75  {
76  Point,
77  Vertex,
78  Agent
79  };
80 
81  /// How the position of the anchor is interpreted.
82  enum class PositionType
83  {
84  WorldSpace,
88  };
89 
90  /// Creates the 'force' and 'distance' primitive attributes.
93  /// Creates the 'torque' and 'angle' primitive attributes.
96  /// Returns the 'constraint_name' primitive attribute.
98 
99  /// Implements filtering of the constraint types that are recognized by the
100  /// caller, as well as caching / reading those constraint's parameter
101  /// values.
102  /// See @ref GenericConstraintAccessor for the default implementation.
104  {
105  public:
106  ConstraintAccessor() = default;
107  virtual ~ConstraintAccessor() = default;
108 
110 
111  /// Initialize with the constraint network's GU_Detail, and the number
112  /// of constraint types referenced by the geometry (from the
113  /// 'constraint_name' primitive attribute).
114  virtual void init(const GU_Detail *gdp, exint num_constraint_types) = 0;
115 
116  /// Returns whether the constraint type should be included when
117  /// iterating over the constraint network, and performs any necessary
118  /// caching with the constraint data and its index.
119  virtual bool acceptConstraint(const SIM_Data &constraint_data,
120  exint idx) = 0;
121  };
122 
123  /// Implements the mapping of anchors to objects recognized by the solver,
124  /// as well as evaluating anchor attributes (position, orientation, etc).
125  /// See @ref GenericAnchorAccessor for the default implementation.
127  {
128  public:
129  AnchorAccessor() = default;
130  virtual ~AnchorAccessor() = default;
131 
133 
134  /// Initialize with the constraint network's GU_Detail and transform.
135  virtual bool init(
136  const GU_Detail &gdp, const UT_Matrix4D &xform,
137  const SIM_Data &container, const SIM_Data &data_root,
138  const SIM_Time &t) = 0;
139  };
140 
141  /// Returns false if there were no valid constraint data types (or nothing
142  /// matched the accessor's filter).
143  /// 'valid_constraint_data' indicates which string indices from the
144  /// constraint_name attribute have valid constraint data.
145  SIM_API bool
147  const GA_ROHandleS &constraint_name_attrib,
148  const SIM_Data &root_data,
149  ConstraintAccessor &constraint_accessor,
150  UT_BitArray &valid_constraint_data);
151 
153  {
154  public:
155  /// @param container A SIM_Relationship or SIM_Object that contains the
156  /// constraint network as subdata.
157  /// @param data_root Parent of the SIM_ConRel subdata.
159  const SIM_Data &container,
160  const SIM_Data &data_root,
161  const SIM_Geometry *geo,
162  const SIM_Time &t,
163  ConstraintAccessor &constraint_accessor,
164  AnchorAccessor &anchor_accessor,
165  bool skip_broken = true);
166 
168  const SIM_Data &container,
169  const SIM_Data &data_root,
170  const SIM_Geometry *geo,
171  const GU_Detail *gdp,
172  const GA_PrimitiveGroup *broken_group,
173  const SIM_Time &t,
174  ConstraintAccessor &constraint_accessor,
175  AnchorAccessor &anchor_accessor,
176  bool skip_broken = true);
177 
179 
180  void rewind();
181  bool atEnd() const;
182  void advance();
183 
184  /// The constraint network geometry.
185  const GU_Detail &getGdp() const { return *myGdp; }
186  /// The number of valid constraints that have been seen so far.
187  exint getConstraintIndex() const { return myConstraintIndex; }
188  /// The primitive index.
189  GA_Index getPrimIndex() const;
190  /// The primitive offset.
191  GA_Offset getPrimOffset() const { return myPrimOffset; }
192 
193  /// Returns whether the constraint is broken and should be inactive.
194  /// Broken constraints are normally skipped by the iterator, unless
195  /// skip_broken is false.
196  bool isBroken() const;
197 
198  /// Returns the index of the current constraint data type.
199  exint getConstraintDataIndex() const;
200 
201  /// Returns whether the constraint affects position / rotation / all.
202  ConstraintType getConstraintType() const;
203 
204  /// The point offset for this anchor in the constraint network geometry.
205  GA_Offset getPointOffset(bool anchor1) const;
206 
207  /// The point number for this anchor in the constraint network geometry.
208  GA_Index getPointNumber(bool anchor1) const;
209 
210  /// Unique id of the current constraint, if the id attribute exists.
212  {
213  return myConstraintIdAttrib.isValid() ?
214  myConstraintIdAttrib.get(myPrimOffset) :
215  -1;
216  }
217 
218  private:
219  void init(
220  const SIM_Data &container, const SIM_Geometry &geo,
221  const GU_Detail &gdp, const SIM_Time &t,
222  ConstraintAccessor &constraint_accessor,
223  AnchorAccessor &anchor_accessor);
224 
225  void skipInvalid();
226 
227  void buildConstraintTypeCache();
228 
229  const SIM_Data &myRootData;
230 
232  const GU_Detail *myGdp;
233 
234  GA_Offset myPrimOffset;
235  exint myConstraintDataIndex;
236  GA_Iterator myPrimIterator;
237 
238  /// Cached offsets for the primitive's two points.
239  GA_Offset myPtAOffset;
240  GA_Offset myPtBOffset;
241 
242  /// Track how many constraints we've seen so far.
243  exint myConstraintIndex;
244 
245  /// Primitive group containing constraints that have been broken.
246  /// These are ignored by the iterator according to mySkipBroken.
247  const GA_PrimitiveGroup *myBrokenGroup;
248  bool mySkipBroken = true;
249 
250  /// Map from the 'constraint_type' attribute's string indices to the
251  /// corresponding ConstraintType enum values.
252  UT_Array<ConstraintType> myConstraintTypes;
253 
254  /// Track whether each unique value of 'constraint_name' is a valid
255  /// constraint data type.
256  UT_BitArray myValidConstraintData;
257 
258  GA_ROHandleS myConstraintNameRef;
259  GA_ROHandleS myConstraintTypeRef;
260  GA_ROHandleI myConstraintIdAttrib;
261  };
262 
263  /// Provides a convenient way to process all of the constraints in the
264  /// network, and update attributes of the constraints.
266  {
267  public:
268  /// Use the filter parameter to process only a certain constraint type.
269  /// The default behaviour is to process everything.
270  iterator(
271  const SIM_Data &container,
272  const SIM_Data &data_root,
273  SIM_GeometryCopy *geo,
274  const SIM_Time &t,
275  ConstraintAccessor &constraint_accessor,
276  AnchorAccessor &anchor_accessor,
277  bool skip_broken = true);
278  ~iterator();
279 
281 
282  void rewind() { return myImpl.rewind(); }
283  bool atEnd() const { return myImpl.atEnd(); }
284  void advance() { myImpl.advance(); }
285 
286  /// Return the number of constraints that have been seen so far.
287  exint getConstraintIndex() const { return myImpl.getConstraintIndex(); }
288 
289  /// Return the primitive number corresponding to the current constraint.
290  GA_Index getPrimIndex() const { return myImpl.getPrimIndex(); }
291  GA_Offset getPrimOffset() const { return myImpl.getPrimOffset(); }
292  bool isBroken() const { return myImpl.isBroken(); }
293 
294  /// Returns the index of the current constraint data type.
295  exint getConstraintDataIndex() const { return myImpl.getConstraintDataIndex(); }
296 
297  /// Return the point number corresponding to an anchor.
298  GA_Index getPointNumber(bool anchor1) const;
299 
300  GU_Detail &getGdp() const { return *myWriteableGdp; }
301 
302  /// Mark the current constraint as being broken.
303  /// This will cause it to switch to the next constraint type (if
304  /// defined) or break and be skipped on any further traversals of the
305  /// constraints. The broken constraints are placed into a primitive
306  /// group, which can then be used in a SOP solver to trigger events
307  /// when constraints break.
308  void breakConstraint();
309 
310  /// Convenience method to update the constraint about the
311  /// forces or torques applied to satisfy it.
312  void updateLinearConstraintState(fpreal force, fpreal distance);
313  void updateAngularConstraintState(fpreal torque, fpreal angle);
314 
315  /// Returns the underlying const_iterator
316  const_iterator &getConstIterator() { return myImpl; }
317 
318  private:
319  static GA_PrimitiveGroup *getBrokenGroup(GU_Detail *gdp);
320 
321  SIM_GeometryCopy *myGeoCopy;
323  GU_Detail *myWriteableGdp;
324  GA_PrimitiveGroup *myBrokenGroup;
325 
326  GA_RWHandleF myForceAttrib;
327  GA_RWHandleF myDistanceAttrib;
328  GA_RWHandleF myTorqueAttrib;
329  GA_RWHandleF myAngleAttrib;
330 
331  GA_RWHandleS myConstraintName;
332  GA_RWHandleS myConstraintType;
333  GA_RWHandleS myNextConstraintName;
334  GA_RWHandleS myNextConstraintType;
335  bool myHasBrokenConstraints;
336 
337  const_iterator myImpl;
338  };
339 
340  /// Generic implementation of @ref ConstraintAccessor, which evaluates
341  /// constraint parameter values by name.
343  {
344  public:
345  /// Default constructor uses SIM_DataFilterAll.
347 
349 
350  void init(const GU_Detail *gdp, exint num_constraint_types) override;
351  bool acceptConstraint(const SIM_Data &constraint_data,
352  exint idx) override;
353 
354  /// Update the current constraint data type index and primitive offset.
355  void advance(exint constraint_idx, GA_Offset primoff);
356 
357  const SIM_Data *getConstraintData() const;
358 
359  /// Returns the value of an attribute of the constraint. If there is no
360  /// primitive attribute with that name, a default value will be taken
361  /// from the attached subdata if possible.
362  template <typename T>
363  T get(const UT_StringRef &attribute_name) const;
364 
365  /// Retrieve a value from the attached subdata for the constraint.
366  template <typename T>
367  T getDataOption(const UT_StringRef &attribute_name) const;
368 
369  /// Looks up a value for a primitive attribute.
370  /// Returns true if the attribute exists, and false otherwise.
371  template <typename T>
372  bool getPrimitiveAttribute(
373  const UT_StringRef &attribute_name, T &value) const;
374 
375  private:
377 
378  /// Looks up a value for a primitive attribute.
379  /// Returns true if the attribute exists, and false otherwise.
380  template <typename T>
381  bool getPrimitiveAttribute(
382  const UT_StringRef &attribute_name, const GU_Detail &gdp,
383  AttribCache &cache, T &value) const;
384 
385  /// Look up an attribute value. If the primitive attribute is not
386  /// present, the value from the subdata will be used.
387  template <typename T>
388  void get(const UT_StringRef &attribute_name, const GU_Detail &gdp,
389  AttribCache &cache, T &value) const;
390 
391  /// Look up a floating-point attribute value. If present, the primitive
392  /// attribute value will be multiplied with the value from the subdata.
393  void get(const UT_StringRef &attribute_name, const GU_Detail &gdp,
394  AttribCache &cache, fpreal &value) const;
395 
396  /// @{
397  /// Helper functions to look up values in a SIM_OptionsUser object.
398  static void getOption(
399  const SIM_OptionsUser *data, const UT_StringRef &attribute_name,
400  int &value)
401  {
402  value = data ? data->getOptions().getOptionI(attribute_name) : 0;
403  }
404  static void getOption(
405  const SIM_OptionsUser *data, const UT_StringRef &attribute_name,
406  fpreal &value)
407  {
408  value = data ? data->getOptions().getOptionF(attribute_name) : 0;
409  }
410 
411  static void getOption(
412  const SIM_OptionsUser *data, const UT_StringRef &attribute_name,
413  UT_Vector2 &value)
414  {
415  value = data ? data->getOptions().getOptionV2(attribute_name) :
416  UT_Vector2D(0, 0);
417  }
418 
419  static void getOption(
420  const SIM_OptionsUser *data, const UT_StringRef &attribute_name,
421  UT_Vector3 &value)
422  {
423  value = data ? data->getOptions().getOptionV3(attribute_name) :
424  UT_Vector3D(0, 0, 0);
425  }
426 
427  static void getOption(
428  const SIM_OptionsUser *data, const UT_StringRef &attribute_name,
429  UT_Quaternion &value)
430  {
431  value = data ? data->getOptions().getOptionQ(attribute_name) :
432  UT_QuaternionD(0, 0, 0, 1);
433  }
434  /// @}
435 
436  const GU_Detail *myGdp;
437  UT_Array<const SIM_Data *> myConstraintData;
438  UT_Array<const SIM_OptionsUser *> myConstraintDataOptions;
439 
440  exint myConstraintDataIndex;
441  GA_Offset myPrimOffset;
442 
443  /// Cache references to attributes.
444  mutable AttribCache myCachedAttributes;
445 
447  };
448 
449  /// Generic implementation of @ref AnchorAccessor, which builds its own
450  /// cache of the objects (transforms, etc) referenced by the anchor points.
452  {
453  public:
454  GenericAnchorAccessor(bool need_object_centroid = false);
455 
457 
458  bool init(
459  const GU_Detail &gdp, const UT_Matrix4D &xform,
460  const SIM_Data &container, const SIM_Data &data_root,
461  const SIM_Time &t) override;
462 
463  void advance(GA_Offset pt_a, GA_Offset pt_b)
464  {
465  myPtAOffset = pt_a;
466  myPtBOffset = pt_b;
467  }
468 
469  /// Get the point offset of the point referred to by the anchor.
470  /// Either the anchor directly references a point on an object,
471  /// or it references a vertex on an object which has an associated
472  /// point. Returns GA_INVALID_OFFSET in the case where the anchor doesnt
473  /// reference a point. If 'anchor_pid' or 'anchor_vid' is present, then
474  /// return the first point (or vertex) with same id as on the
475  /// constraint.
476  GA_Offset getAnchorPointOffset(bool anchor1) const;
477 
478  /// Gets the point index of the point referred to by the anchor in a
479  /// similar way to getAnchorPointOffset.
480  GA_Index getAnchorPointIndex(bool anchor1) const;
481 
482  UT_Vector3 getAnchorPosition(bool anchor1) const;
483  UT_Vector3 getAnchorVelocity(bool anchor1) const;
484  /// Returns the type of position described by the anchor (e.g. relative
485  /// offset).
486  PositionType getAnchorPositionType(bool anchor1) const;
487 
488  UT_Quaternion getAnchorOrientation(bool anchor1) const;
489  UT_Vector3 getAnchorAngularVelocity(bool anchor1) const;
490 
491  bool getObjectCentroid(bool anchor1, UT_Vector3 &pos) const;
492 
493  int getAnchorNumConDOFs(bool anchor1) const;
494  UT_Vector3 getAnchorDOFVector(bool anchor1) const;
495 
496  const UT_StringHolder &getAnchorName(bool anchor1) const;
497 
498  /// Returns the id of the object named by the anchor.
499  /// Returns -1 of the anchor does not name an object.
500  int getObjectId(bool anchor1) const;
501  const SIM_Object *getObject(bool anchor1) const;
502 
503  private:
504  class CachedObjectData
505  {
506  public:
507  CachedObjectData(const SIM_BaseObjectReader &reader,
508  bool need_object_geo,
509  bool need_object_centroid,
510  const UT_Matrix4D *agent_xform);
511  ~CachedObjectData() {}
512 
513  const GU_Detail *getGdp() const
514  {
515  if (myGeoReadLock)
516  {
517  const GU_Detail *gdp = myGeoReadLock->getGdp();
518  UT_ASSERT(gdp);
519  return gdp;
520  }
521  else
522  return 0;
523  }
524 
525  const SIM_Object &myObject;
527 
528  UT_DMatrix4 myPosXform;
529  UT_DMatrix4 myGeoXform;
530  UT_Matrix4D myAgentXform;
531  UT_Quaternion myPosRotation;
532  UT_Quaternion myGeoRotation;
533  UT_Quaternion myAgentRotation;
534  const UT_Vector3 myCentroid;
535 
536  GA_ROHandleQ myOrientRef;
537  GA_ROHandleV3 myVelocityRef;
538  GA_ROHandleV3 myWRef;
539 
540  GA_ROHandleI myAnchorPIDRef;
541  GA_ROHandleI myAnchorVIDRef;
542  };
543 
544  /// Get the point offset of the point referenced by the anchor.
545  /// Either the anchor directly references a point on an object,
546  /// or it references a vertex on an object which has an associated
547  /// point. Returns GA_INVALID_OFFSET in the case where the anchor doesnt
548  /// reference a point
549  GA_Offset
550  getAnchorPointOffset(bool anchor1, const CachedObjectData *data) const;
551 
552  /// Returns the cached data for an anchor by looking up the name
553  const CachedObjectData *getCachedData(bool anchor1) const;
554 
555  /// Get the value in the anchor's anchor_id attribute
556  /// (-1 if no such attribute exists)
557  int getAnchorId(bool anchor1) const;
558 
559  /// Get the type of the Anchor (Point or Vertex)
560  AnchorType getAnchorType(bool anchor1) const;
561 
562  UT_Quaternion getInitialAnchorOrientation(bool anchor1) const;
563 
564  GA_StringIndexType getAnchorNameIndex(bool anchor1) const;
565 
566  SYS_FORCE_INLINE GA_Offset getPointOffset(bool anchor1) const
567  {
568  return anchor1 ? myPtBOffset : myPtAOffset;
569  }
570 
571  const GU_Detail *myGdp;
572 
573  GA_Offset myPtAOffset;
574  GA_Offset myPtBOffset;
575 
576  /// Mapping from object names to cached data necessary for certain
577  /// functions
579 
580  UT_DMatrix4 myXform;
581  UT_Quaternion myOrient;
582  const SIM_Data *myRootData;
583  bool myNeedObjectCentroid;
584 
585  GA_ROHandleS myNameRef;
586  GA_ROHandleI myNumDOFsRef;
587  GA_ROHandleV3 myDOFVectorRef;
588  GA_ROHandleV3 myLocalPosRef;
589  GA_ROHandleQ myLocalOrientRef;
590  GA_ROHandleI myAnchorIdRef;
591  GA_ROHandleS myAnchorTypeRef;
592  GA_ROHandleV3 myVelocityRef;
593  GA_ROHandleV3 myRotationRef;
594  GA_ROHandleQ myOrientRef;
595  GA_ROHandleV3 myAngularVelocityRef;
596  };
597 
598 }
599 
601 
602 #endif
SIM_API const UT_StringHolder vertex
SIM_API const UT_StringHolder anchor_type
PositionType
How the position of the anchor is interpreted.
GA_Index getPrimIndex() const
Return the primitive number corresponding to the current constraint.
const UT_Vector2D & getOptionV2(const UT_StringRef &name) const
const_iterator & getConstIterator()
Returns the underlying const_iterator.
Iteration over a range of elements.
Definition: GA_Iterator.h:29
SIM_API const UT_StringHolder angle
UT_Vector2T< fpreal64 > UT_Vector2D
SIM_API const UT_StringHolder agent
GLsizei const GLfloat * value
Definition: glcorearb.h:824
SIM_API const UT_StringHolder torque
exint getConstraintIndex() const
The number of valid constraints that have been seen so far.
SIM_API const UT_StringHolder local_P
int64 exint
Definition: SYS_Types.h:125
int64 getOptionI(const UT_StringRef &name) const
SIM_API const UT_StringHolder next_constraint_type
const UT_Vector3D & getOptionV3(const UT_StringRef &name) const
SIM_API const UT_StringHolder local_orient
SIM_API bool buildConstraintDataCache(const GU_Detail &gdp, const GA_ROHandleS &constraint_name_attrib, const SIM_Data &root_data, ConstraintAccessor &constraint_accessor, UT_BitArray &valid_constraint_data)
SIM_API const UT_StringHolder all
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
GA_Size GA_Offset
Definition: GA_Types.h:646
exint getConstraintId() const
Unique id of the current constraint, if the id attribute exists.
SIM_API void addTorqueAttributes(GU_Detail &gdp, GA_RWHandleF &torque, GA_RWHandleF &angle)
Creates the 'torque' and 'angle' primitive attributes.
exint getConstraintDataIndex() const
Returns the index of the current constraint data type.
SIM_API void addForceAttributes(GU_Detail &gdp, GA_RWHandleF &force, GA_RWHandleF &distance)
Creates the 'force' and 'distance' primitive attributes.
UT_QuaternionT< fpreal64 > UT_QuaternionD
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
SIM_API const UT_StringHolder next_constraint_name
SIM_API const UT_StringHolder broken
SIM_API const UT_StringHolder rotation
UT_Vector3T< fpreal64 > UT_Vector3D
const UT_QuaternionD & getOptionQ(const UT_StringRef &name) const
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:640
const GU_Detail & getGdp() const
The constraint network geometry.
GLdouble t
Definition: glad.h:2397
const SIM_Options & getOptions() const
SIM_API GA_ROHandleS getConstraintNameAttrib(const GU_Detail &gdp)
Returns the 'constraint_name' primitive attribute.
ConstraintType
The type of a constraint (linear, angular, or both).
exint getConstraintIndex() const
Return the number of constraints that have been seen so far.
SIM_API const UT_StringHolder force
fpreal64 fpreal
Definition: SYS_Types.h:277
SIM_API const UT_StringHolder anchor_vid
SIM_API const UT_StringHolder position
#define SIM_API
Definition: SIM_API.h:12
SIM_API const UT_StringHolder constraint_type
GA_BlobIndex GA_StringIndexType
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
Definition: core.h:1131
GA_Offset getPrimOffset() const
The primitive offset.
SIM_API const UT_StringHolder distance
SIM_API const UT_StringHolder anchor_pid
SIM_API const UT_StringHolder constraint_name
fpreal64 getOptionF(const UT_StringRef &name) const
Definition: format.h:895
SIM_API const UT_StringHolder anchor_id
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
This implements a SIM_Geometry that copies the source geometry.