roavatar-renderer 1.5.8 → 1.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,8 @@ Basic example on how to load an avatar using OutfitRenderer (to make it simpler)
17
17
  **TYPESCRIPT:**
18
18
  ```ts
19
19
  //setup flags that are compatible with you environment
20
- FLAGS.ONLINE_ASSETS = true //set false to true if you want assets to be loaded locally
20
+ FLAGS.FETCH_FUNC = undefined //undefined is the default fetch() function, this flag can be used to intercept requests
21
+ FLAGS.ONLINE_ASSETS = true //set true to false if you want assets to be loaded locally
21
22
 
22
23
  //if we arent using online assets we have to provide the renderer with the paths
23
24
  if (!FLAGS.ONLINE_ASSETS) {
@@ -55,5 +56,7 @@ if (!(outfit instanceof Outfit)) throw new Error("Failed to get outfit")
55
56
  outfitRenderer.startAnimating()
56
57
  outfitRenderer.setMainAnimation("idle")
57
58
  ```
59
+ **RESULT:**
60
+ <img src="https://devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/5X/9/f/a/d/9fadf25d9770b63e8a2e480369930cad94ad04aa.png">
58
61
 
59
62
  Also the OutfitRenderer code or RoAvatar source code is useful, especially ```avatarPreview.ts```
package/dist/index.d.ts CHANGED
@@ -114,7 +114,7 @@ export declare const AlphaMode: {
114
114
  /**
115
115
  * @category InstanceWrapper
116
116
  */
117
- export declare class AnimationConstraintWrapper extends InstanceWrapper {
117
+ export declare class AnimationConstraintWrapper extends ConstraintWrapper {
118
118
  static className: string;
119
119
  static requiredProperties: string[];
120
120
  setup(): void;
@@ -332,6 +332,13 @@ export declare const API: {
332
332
  };
333
333
  Asset: {
334
334
  GetAssetBuffer: (url: string, headers?: HeadersInit, extraStr?: string) => Promise<Response | ArrayBuffer>;
335
+ /**
336
+ * Remember to call .Destroy() on the Instance returned by RBX.generateTree() to avoid memory leaks
337
+ * @param url
338
+ * @param headers
339
+ * @param contentRepresentationPriorityList
340
+ * @returns
341
+ */
335
342
  GetRBX: (url: string, headers?: HeadersInit, contentRepresentationPriorityList?: any) => Promise<Response | RBX>;
336
343
  GetMesh: (url: string, headers?: HeadersInit, readOnly?: boolean) => Promise<FileMesh | Response>;
337
344
  IsLayered: (id: number) => Promise<boolean | Response>;
@@ -348,6 +355,9 @@ export declare const API: {
348
355
  GetMarketplaceWidgetsSearch: (query: string) => Promise<MarketplaceWidgets_Result | Response>;
349
356
  GetMarketplaceWidgets: (context?: string) => Promise<MarketplaceWidgets_Result | Response>;
350
357
  };
358
+ Develop: {
359
+ GetLatestVersions: (auth: Authentication, assetIds: number[]) => Promise<Response | LatestVersions_Result>;
360
+ };
351
361
  Inventory: {
352
362
  GetInventory: (userId: number, assetType: number, cursor?: string) => Promise<Response>;
353
363
  IsItemOwned: (userId: number, itemType: string, assetId: number) => Promise<any>;
@@ -387,6 +397,45 @@ export declare const API: {
387
397
 
388
398
  export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
389
399
 
400
+ export declare class Assembly {
401
+ id: number;
402
+ rootNode: AssemblyNode;
403
+ allNodes: AssemblyNode[] | undefined;
404
+ allConnectors: Instance[] | undefined;
405
+ constructor(rootPart: Instance);
406
+ getNodeDescendants(): AssemblyNode[];
407
+ getPartDescendants(): Instance[];
408
+ getAllConnectors(): Instance[];
409
+ getNode(name: string): AssemblyNode | undefined;
410
+ getPart(name: string): Instance | undefined;
411
+ traverseTree(): void;
412
+ traverseCFrame(node: AssemblyNode, includeTransform: boolean, applyRoot?: boolean): CFrame;
413
+ traverseInstance(node: AssemblyNode): Instance[];
414
+ destroy(): void;
415
+ }
416
+
417
+ export declare class AssemblyNode {
418
+ depth: number;
419
+ nodes: AssemblyNode[];
420
+ part: Instance;
421
+ children: Instance[];
422
+ connectors: Instance[];
423
+ parent: AssemblyNode | Assembly;
424
+ assembly: Assembly;
425
+ traversedRestCFrame: CFrame;
426
+ traversedTransformCFrame: CFrame;
427
+ constructor(assembly: Assembly, parent: AssemblyNode | Assembly, depth: number, part: Instance, already?: Instance[]);
428
+ getNodeChildren(): AssemblyNode[];
429
+ getNodeDescendants(): AssemblyNode[];
430
+ getParentConnector(): Instance | undefined;
431
+ getConnectorOffset(includeTransform: boolean): CFrame;
432
+ _traverseTree(transform: CFrame, rest: CFrame): void;
433
+ /**
434
+ * Should only be called when destroying entire assembly by calling Assembly.destroy()
435
+ */
436
+ destroy(): void;
437
+ }
438
+
390
439
  /** @category Outfit */
391
440
  export declare class Asset {
392
441
  id: number;
@@ -503,7 +552,7 @@ export declare class AttachmentWrapper extends InstanceWrapper {
503
552
  static className: string;
504
553
  static requiredProperties: string[];
505
554
  setup(): void;
506
- getWorldCFrame(): CFrame;
555
+ getWorldCFrame(includeTransform?: boolean): CFrame;
507
556
  }
508
557
 
509
558
  /** @category API */
@@ -614,6 +663,26 @@ declare class BaseKeyframeGroup {
614
663
  getHigherKeyframe(time: number): BaseKeyframe | null;
615
664
  }
616
665
 
666
+ /**
667
+ * @category InstanceWrapper
668
+ */
669
+ export declare class BasePartWrapper extends InstanceWrapper {
670
+ static className: string;
671
+ static requiredProperties: string[];
672
+ setup(): void;
673
+ get data(): BasePartWrapperData;
674
+ created(): void;
675
+ preRender(): void;
676
+ GetAssemblyNode(): AssemblyNode;
677
+ GetAssembly(): Assembly;
678
+ GetConnectors(): Instance[];
679
+ GetConnectedParts(): Instance[];
680
+ }
681
+
682
+ declare class BasePartWrapperData {
683
+ assemblyNode?: AssemblyNode;
684
+ }
685
+
617
686
  /**@category Outfit */
618
687
  export declare type BodyColor3Name = "headColor3" | "torsoColor3" | "rightArmColor3" | "leftArmColor3" | "rightLegColor3" | "leftLegColor3";
619
688
 
@@ -724,6 +793,15 @@ export declare const BodyPartNameToEnum: {
724
793
  [K in string]: number;
725
794
  };
726
795
 
796
+ /**
797
+ * @category InstanceWrapper
798
+ */
799
+ export declare class BoneWrapper extends AttachmentWrapper {
800
+ static className: string;
801
+ static requiredProperties: string[];
802
+ setup(): void;
803
+ }
804
+
727
805
  /**
728
806
  * @category Mesh
729
807
  */
@@ -795,18 +873,29 @@ export declare interface BundleDetails_Result {
795
873
  export declare const BundleTypes: string[];
796
874
 
797
875
  export declare const CACHE: {
798
- AssetBuffer: Map<string, Promise<ArrayBuffer | Response>>;
799
- RBX: Map<string, RBX>;
800
- Mesh: Map<string, FileMesh>;
801
- Image: Map<string, HTMLImageElement | Promise<HTMLImageElement | undefined> | undefined>;
802
- Thumbnails: Map<string, string | undefined>;
803
- ItemOwned: Map<string, [boolean, number]>;
804
- IsLayered: Map<number, boolean>;
805
- AvatarInventoryItem: Map<string, AvatarInventory_Result>;
806
- ItemDetails: Map<string, ItemDetail_Result>;
876
+ AssetBuffer: Cache_2<string, Promise<ArrayBuffer | Response>>;
877
+ RBX: Cache_2<string, RBX>;
878
+ Mesh: Cache_2<string, FileMesh>;
879
+ Image: Cache_2<string, HTMLImageElement | Promise<HTMLImageElement | undefined> | undefined>;
880
+ Thumbnails: Cache_2<string, string | undefined>;
881
+ ItemOwned: Cache_2<string, [boolean, number]>;
882
+ IsLayered: Cache_2<number, boolean>;
883
+ AvatarInventoryItem: Cache_2<string, AvatarInventory_Result>;
884
+ ItemDetails: Cache_2<string, ItemDetail_Result>;
807
885
  UserInfo: undefined;
808
886
  };
809
887
 
888
+ declare class Cache_2<K, V> {
889
+ map: Map<K, V>;
890
+ lastAccess: Map<K, number>;
891
+ maxEntries: number;
892
+ constructor(maxEntries?: number);
893
+ get(key: K): V | undefined;
894
+ set(key: K, value: V): Cache_2<K, V>;
895
+ has(key: K): boolean;
896
+ delete(key: K): boolean;
897
+ }
898
+
810
899
  /**
811
900
  * @category Mesh
812
901
  */
@@ -929,6 +1018,15 @@ export declare class Connection {
929
1018
  Disconnect(): void;
930
1019
  }
931
1020
 
1021
+ /**
1022
+ * @category InstanceWrapper
1023
+ */
1024
+ export declare class ConstraintWrapper extends InstanceWrapper {
1025
+ static className: string;
1026
+ static requiredProperties: string[];
1027
+ setup(): void;
1028
+ }
1029
+
932
1030
  /**
933
1031
  * @category DataModelProperty
934
1032
  */
@@ -1212,6 +1310,8 @@ export declare class FileMesh {
1212
1310
  fromBuffer(buffer: ArrayBuffer): Promise<void>;
1213
1311
  stripLODS(): void;
1214
1312
  padSkinnings(): void;
1313
+ setBoneSource(name: string): void;
1314
+ setBoneSourceSize(): void;
1215
1315
  combine(other: FileMesh): void;
1216
1316
  removeDuplicateVertices(distance?: number): number;
1217
1317
  removeFaces(indices: number[]): void;
@@ -1223,24 +1323,20 @@ export declare class FileMesh {
1223
1323
  * Bone inside SKINNING in FileMesh
1224
1324
  * @category Mesh */
1225
1325
  export declare class FileMeshBone {
1326
+ name?: string;
1226
1327
  boneNameIndex: number;
1227
1328
  parentIndex: number;
1228
1329
  lodParentIndex: number;
1229
1330
  culling: number;
1230
1331
  rotationMatrix: Mat3x3;
1231
1332
  position: Vec3;
1333
+ sourcePart?: string;
1334
+ sourceOffset: CFrame;
1335
+ sourceSize?: Vec3;
1336
+ sourceScaled: Vec3;
1232
1337
  clone(): FileMeshBone;
1233
1338
  }
1234
1339
 
1235
- /**
1236
- * Contains bone weights for a single vert
1237
- * @category Mesh */
1238
- export declare class FileMeshSkinning {
1239
- subsetIndices: Vec4;
1240
- boneWeights: Vec4;
1241
- clone(): FileMeshSkinning;
1242
- }
1243
-
1244
1340
  /**
1245
1341
  * Section of mesh inside SKINNING in FileMesh
1246
1342
  * @category Mesh */
@@ -1271,6 +1367,10 @@ export declare const FLAGS: {
1271
1367
  AVATAR_JOINT_UPGRADE: boolean;
1272
1368
  /**wip, only for memory leak debugging */
1273
1369
  INSTANCE_GARBAGE_COLLECT: boolean;
1370
+ /**makes welds immediately update (bad for performance) */
1371
+ LEGACY_WELD_BEHAVIOR: boolean;
1372
+ /**caches hierarchy of joints and removes directionality (required for AVATAR_JOINT_UPGRADE)*/
1373
+ USE_ASSEMBLY: boolean;
1274
1374
  /**outfits returned by api use Color3 instead of BrickColor */
1275
1375
  BODYCOLOR3: boolean;
1276
1376
  /**allows API to cache data */
@@ -1293,7 +1393,10 @@ export declare const FLAGS: {
1293
1393
  API_REQUEST_PREFIX: string;
1294
1394
  /**credentials request type when fetching from API_DOMAIN and credentials are usually include */
1295
1395
  INCLUDE_REQUEST_CREDENTIALS_OVERRIDE: RequestCredentials;
1396
+ /**if requests should be retried once after failing */
1296
1397
  API_REQUEST_RETRY: boolean;
1398
+ /**the fetch function the api will use */
1399
+ FETCH_FUNC: ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>) | undefined;
1297
1400
  /**loads assets from assetdelivery instead of local files */
1298
1401
  ONLINE_ASSETS: boolean;
1299
1402
  /**path to rbxasset:// local files*/
@@ -1330,8 +1433,6 @@ export declare const FLAGS: {
1330
1433
  GEAR_ENABLED: boolean;
1331
1434
  /**makes Audio instances play sound when played */
1332
1435
  AUDIO_ENABLED: boolean;
1333
- /**makes welds immediately update (bad for performance) */
1334
- LEGACY_WELD_BEHAVIOR: boolean;
1335
1436
  /**enables full texture compilation using ThreeJS RenderTarget */
1336
1437
  USE_RENDERTARGET: boolean;
1337
1438
  /**the renderer will attempt to restore the webgl context when it is lost */
@@ -1342,15 +1443,21 @@ export declare const FLAGS: {
1342
1443
  THUMBNAIL_TIMEOUT: number;
1343
1444
  /**Always render attachments even when theyre set to not be visible */
1344
1445
  ALWAYS_SHOW_ATTACHMENTS: boolean;
1446
+ /**Multiplier for delta time used by renderer things (does not affect datamodel side of things but particles and such instead) */
1447
+ RENDERER_DELTA_TIME_MULTIPLIER: number;
1345
1448
  /**shows ThreeJS SkeletonHelper */
1346
1449
  SHOW_SKELETON_HELPER: boolean;
1450
+ /**if set the skeleton helper will only show if the instance has the name */
1451
+ SKELETON_HELPER_INSTANCE_NAME: string | undefined;
1347
1452
  /**skeleton is updated every frame */
1348
1453
  UPDATE_SKELETON: boolean;
1349
1454
  /**skeleton is animated every frame */
1350
1455
  ANIMATE_SKELETON: boolean;
1351
1456
  /**autoskin is applied even when its disabled */
1352
1457
  AUTO_SKIN_EVERYTHING: boolean;
1353
- /**skeleton is local instead of global (broken) */
1458
+ /**skeleton is local instead of global (broken)
1459
+ * @deprecated No longer does anything
1460
+ */
1354
1461
  USE_LOCAL_SKELETONDESC: boolean;
1355
1462
  /**enables HSR (hidden surface removal) */
1356
1463
  ENABLE_HSR: boolean;
@@ -1530,10 +1637,16 @@ export declare function getOriginalAttachmentPosition(attachment: Instance): Vec
1530
1637
 
1531
1638
  export declare function getOriginalSize(part: Instance): Vector3;
1532
1639
 
1640
+ export declare function getPartAssemblyScore(part: Instance): number;
1641
+
1642
+ export declare function getPartsInAssembly_Generate(part: Instance): Instance[];
1643
+
1533
1644
  export declare function getRandomBetweenInclusive(min: number, max: number, randomVal?: number): number;
1534
1645
 
1535
1646
  export declare function getRigExtentsWorld(rig: Instance): [Vector3, Vector3];
1536
1647
 
1648
+ export declare function getRootAssemblyPart_Generate(part: Instance): Instance;
1649
+
1537
1650
  /**@category API */
1538
1651
  export declare interface GetSubscription_Result {
1539
1652
  "subscriptionProductModel": {
@@ -1779,6 +1892,7 @@ export declare class Instance {
1779
1892
  parent?: Instance;
1780
1893
  destroyed: boolean;
1781
1894
  private _hasWrappered;
1895
+ wrapperInitialized: boolean;
1782
1896
  classID?: number;
1783
1897
  objectFormat?: number;
1784
1898
  ChildAdded: Event_2;
@@ -1786,6 +1900,7 @@ export declare class Instance {
1786
1900
  Destroying: Event_2;
1787
1901
  Changed: Event_2;
1788
1902
  AncestryChanged: Event_2;
1903
+ referencedByChanged: Event_2;
1789
1904
  constructor(className: string, notComplete?: boolean);
1790
1905
  get id(): string;
1791
1906
  set name(value: string);
@@ -1796,6 +1911,7 @@ export declare class Instance {
1796
1911
  removeConnectionReference(connection: Connection): void;
1797
1912
  addReferencedBy(instance: Instance): void;
1798
1913
  removeReferencedBy(instance: Instance): void;
1914
+ getReferencedBy(): Instance[];
1799
1915
  addProperty(property: Property, value?: unknown): void;
1800
1916
  fixPropertyName(name: string): string;
1801
1917
  setProperty(name: string, value: unknown, supressEvents?: boolean): void;
@@ -1829,6 +1945,7 @@ export declare class Instance {
1829
1945
  Child(name: string): Instance | undefined;
1830
1946
  FindFirstChildOfClass(className: string): Instance | undefined;
1831
1947
  FindLastChildOfClass(className: string): Instance | undefined;
1948
+ IsA(className: string): boolean;
1832
1949
  preRender(): void;
1833
1950
  }
1834
1951
 
@@ -1949,6 +2066,26 @@ export declare class ItemSort {
1949
2066
 
1950
2067
  declare type ItemType = "Asset" | "Bundle" | "Outfit" | "Look" | "Avatar" | "None";
1951
2068
 
2069
+ /**
2070
+ * @category InstanceWrapper
2071
+ */
2072
+ export declare class JointInstanceWrapper extends InstanceWrapper {
2073
+ static className: string;
2074
+ static requiredProperties: string[];
2075
+ setup(): void;
2076
+ get data(): JointInstanceWrapperData;
2077
+ created(): void;
2078
+ preRender(): void;
2079
+ getAssemblyOffset(includeTransform: boolean): CFrame;
2080
+ update(affectedPart?: number): void;
2081
+ }
2082
+
2083
+ declare class JointInstanceWrapperData {
2084
+ part0ChangedConnection?: Connection;
2085
+ lastUpdateTime: number;
2086
+ timeUpdates: number;
2087
+ }
2088
+
1952
2089
  declare class KDNode {
1953
2090
  point: Vec3;
1954
2091
  index: number;
@@ -1958,6 +2095,15 @@ declare class KDNode {
1958
2095
  constructor(point: Vec3, index: number, axis: number);
1959
2096
  }
1960
2097
 
2098
+ /**@category API */
2099
+ export declare interface LatestVersions_Result {
2100
+ "results": {
2101
+ assetId: number;
2102
+ status: "Success" | string;
2103
+ versionNumber: number;
2104
+ }[];
2105
+ }
2106
+
1961
2107
  /**
1962
2108
  * @deprecated Superseded by RBFDeformerPatch
1963
2109
  * @category Mesh
@@ -2145,7 +2291,7 @@ export declare const MakeupType: {
2145
2291
  /**
2146
2292
  * @category InstanceWrapper
2147
2293
  */
2148
- export declare class ManualWeldWrapper extends WeldWrapper {
2294
+ export declare class ManualWeldWrapper extends JointInstanceWrapper {
2149
2295
  static className: string;
2150
2296
  }
2151
2297
 
@@ -2225,6 +2371,15 @@ declare class MeshCollider {
2225
2371
  raycast(ray: Ray_2): boolean;
2226
2372
  }
2227
2373
 
2374
+ /**
2375
+ * @category InstanceWrapper
2376
+ */
2377
+ export declare class MeshPartWrapper extends BasePartWrapper {
2378
+ static className: string;
2379
+ static requiredProperties: string[];
2380
+ setup(): void;
2381
+ }
2382
+
2228
2383
  /**@category DataModelEnum */
2229
2384
  export declare const MeshType: {
2230
2385
  Brick: number;
@@ -2257,7 +2412,7 @@ export declare class ModelWrapper extends InstanceWrapper {
2257
2412
  /**
2258
2413
  * @category InstanceWrapper
2259
2414
  */
2260
- export declare class Motor6DWrapper extends WeldWrapper {
2415
+ export declare class Motor6DWrapper extends JointInstanceWrapper {
2261
2416
  static className: string;
2262
2417
  static requiredProperties: string[];
2263
2418
  setup(): void;
@@ -2590,7 +2745,7 @@ export declare class OutfitRenderer {
2590
2745
  /**
2591
2746
  * Updates the current rig, called internally by _updateOutfit()
2592
2747
  */
2593
- _setRigTo(newRigType: AvatarType): Promise<Instance | Response>;
2748
+ _setRigTo(newRigType: AvatarType): Promise<Response | Instance>;
2594
2749
  /**
2595
2750
  * Rerenders the current outfit, called internally by setOutfit() and constructor
2596
2751
  */
@@ -2684,6 +2839,15 @@ export declare const PartType: {
2684
2839
  CornerWedge: number;
2685
2840
  };
2686
2841
 
2842
+ /**
2843
+ * @category InstanceWrapper
2844
+ */
2845
+ export declare class PartWrapper extends BasePartWrapper {
2846
+ static className: string;
2847
+ static requiredProperties: string[];
2848
+ setup(): void;
2849
+ }
2850
+
2687
2851
  declare class PRNT {
2688
2852
  instanceCount: number;
2689
2853
  childReferents: number[];
@@ -3305,21 +3469,24 @@ declare class SimpleView {
3305
3469
  readUint8(): number;
3306
3470
  }
3307
3471
 
3308
- /**
3309
- * Class inside FileMesh that contains skinning data
3310
- * @category Mesh */
3311
3472
  export declare class SKINNING {
3312
- numSkinnings: number;
3313
- skinnings: FileMeshSkinning[];
3314
- numBones: number;
3473
+ _numskinnings: number;
3474
+ private _indices;
3475
+ private _weights;
3315
3476
  bones: FileMeshBone[];
3316
- nameTableSize: number;
3317
- nameTable: string[];
3318
- numSubsets: number;
3319
- subsets: FileMeshSubset[];
3320
3477
  clone(): SKINNING;
3321
- getBone(boneName: string): FileMeshBone | undefined;
3322
- getSubsetIndex(vertIndex: number): number;
3478
+ get numskinnings(): number;
3479
+ set numskinnings(value: number);
3480
+ get indices(): Uint16Array<ArrayBufferLike>;
3481
+ get weights(): Float32Array<ArrayBufferLike>;
3482
+ getIndex(i: number): Vec4;
3483
+ setIndex(i: number, value: Vec4): void;
3484
+ getWeight(i: number): Vec4;
3485
+ setWeight(i: number, value: Vec4): void;
3486
+ readSkinning(view: SimpleView, i: number): void;
3487
+ increaseSkinnings(add: number): void;
3488
+ onlySkinnings(only: number[]): void;
3489
+ getBone(name: string): FileMeshBone | undefined;
3323
3490
  }
3324
3491
 
3325
3492
  export declare function snapToNumber(value: number, numbers: number[]): number;
@@ -3590,6 +3757,13 @@ export declare function Wait(time: number): Promise<unknown>;
3590
3757
  /** @category Outfit */
3591
3758
  export declare const WearableAssetTypes: string[];
3592
3759
 
3760
+ /**
3761
+ * @category InstanceWrapper
3762
+ */
3763
+ export declare class WedgePartWrapper extends BasePartWrapper {
3764
+ static className: string;
3765
+ }
3766
+
3593
3767
  /**
3594
3768
  * @category Mesh
3595
3769
  */
@@ -3608,20 +3782,8 @@ export declare type WeightChunk3 = WeightChunk & {
3608
3782
  /**
3609
3783
  * @category InstanceWrapper
3610
3784
  */
3611
- export declare class WeldWrapper extends InstanceWrapper {
3785
+ export declare class WeldWrapper extends JointInstanceWrapper {
3612
3786
  static className: string;
3613
- static requiredProperties: string[];
3614
- setup(): void;
3615
- get data(): WeldWrapperData;
3616
- created(): void;
3617
- preRender(): void;
3618
- update(affectedPart?: number): void;
3619
- }
3620
-
3621
- declare class WeldWrapperData {
3622
- part0ChangedConnection?: Connection;
3623
- lastUpdateTime: number;
3624
- timeUpdates: number;
3625
3787
  }
3626
3788
 
3627
3789
  export declare const WorkerTypeToFunction: {