// Copyright (c) 2016-2024, The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0
[[spaces]]
== Spaces

[open,refpage='XrSpace',desc='Opaque handle to a space',type='handles',xrefs='xrCreateReferenceSpace xrCreateActionSpace XrReferenceSpaceCreateInfo XrActionSpaceCreateInfo xrDestroySpace']
--
Across both virtual reality and augmented reality, XR applications have a
core need to map the location of virtual objects to the corresponding
real-world locations where they will be rendered.
**Spaces** allow applications to explicitly create and specify the frames of
reference in which they choose to track the real world, and then determine
how those frames of reference move relative to one another over time.

include::{generated}/api/handles/XrSpace.adoc[]

Spaces are represented by slink:XrSpace handles, which the application
creates and then uses in API calls.
Whenever an application calls a function that returns coordinates, it
provides an slink:XrSpace to specify the frame of reference in which those
coordinates will be expressed.
Similarly, when providing coordinates to a function, the application
specifies which slink:XrSpace the runtime should use to interpret those
coordinates.

OpenXR defines a set of well-known **reference spaces** that applications
use to bootstrap their spatial reasoning.
These reference spaces are: `VIEW`, `LOCAL`, `LOCAL_FLOOR`, and `STAGE`.
Each reference space has a well-defined meaning, which establishes where its
origin is positioned and how its axes are oriented.

Runtimes whose tracking systems improve their understanding of the world
over time may: track spaces independently.
For example, even though a {local_space} and a {stage_space} each map their
origin to a static position in the world, a runtime with an inside-out
tracking system may: introduce slight adjustments to the origin of each
space on a continuous basis to keep each origin in place.

Beyond well-known reference spaces, runtimes expose other
independently-tracked spaces, such as a pose action space that tracks the
pose of a motion controller over time.

When one or both spaces are tracking a dynamic object, passing in an updated
time to flink:xrLocateSpace each frame will result in an updated relative
pose.
For example, the location of the left hand's pose action space in the
`STAGE` reference space will change each frame as the user's hand moves
relative to the stage's predefined origin on the floor.
In other XR APIs, it is common to report the "pose" of an object relative to
some presumed underlying global space.
This API is careful to not explicitly define such an underlying global
space, because it does not apply to all systems.
Some systems will support no {stage_space}, while others may support a
{stage_space} that switches between various physical stages with dynamic
availability.
To satisfy this wide variability, "poses" are always described as the
relationship between two spaces.

Some devices improve their understanding of the world as the device is used.
The location returned by flink:xrLocateSpace in later frames may: change
over time, even for spaces that track static objects, as either the target
space or base space adjusts its origin.

Composition layers submitted by the application include an slink:XrSpace for
the runtime to use to position that layer over time.
Composition layers whose slink:XrSpace is relative to the `VIEW` reference
space are implicitly "head-locked", even if they may not be "display-locked"
for non-head-mounted form factors.
--

[[spaces-reference-spaces]]
=== Reference Spaces

[open,refpage='XrReferenceSpaceType',desc='Reference space types',type='enums']
--
The elink:XrReferenceSpaceType enumeration is defined as:

include::{generated}/api/enums/XrReferenceSpaceType.adoc[]

Brief introductions to core reference space types follow.
Each has full requirements in a subsequent section, linked from these
descriptions.

.Enumerant Descriptions
****
ifdef::doctype-manpage[]
* xref:XR_REFERENCE_SPACE_TYPE_VIEW.adoc[ename:XR_REFERENCE_SPACE_TYPE_VIEW].
endif::doctype-manpage[]
ifndef::doctype-manpage[]
* <<XR_REFERENCE_SPACE_TYPE_VIEW,ename:XR_REFERENCE_SPACE_TYPE_VIEW>>.
endif::doctype-manpage[]
  The `VIEW` reference space tracks the view origin used to generate view
  transforms for the primary viewer (or centroid of view origins if stereo),
  with +Y up, +X to the right, and -Z forward.
  This space points in the forward direction for the viewer without
  incorporating the user's eye orientation, and is not gravity-aligned.
+
Runtimes must: support `VIEW` reference space.

ifdef::doctype-manpage[]
* xref:XR_REFERENCE_SPACE_TYPE_LOCAL.adoc[ename:XR_REFERENCE_SPACE_TYPE_LOCAL].
endif::doctype-manpage[]
ifndef::doctype-manpage[]
* <<XR_REFERENCE_SPACE_TYPE_LOCAL,ename:XR_REFERENCE_SPACE_TYPE_LOCAL>>.
endif::doctype-manpage[]
  The `LOCAL` reference space establishes a world-locked origin,
  gravity-aligned to exclude pitch and roll, with +Y up, +X to the right,
  and -Z forward.
  This space locks in both its initial position and orientation, which the
  runtime may: define to be either the initial position at application
  launch or some other calibrated zero position.
+
Runtimes must: support `LOCAL` reference space.

ifdef::doctype-manpage[]
* xref:XR_REFERENCE_SPACE_TYPE_STAGE.adoc[ename:XR_REFERENCE_SPACE_TYPE_STAGE].
endif::doctype-manpage[]
ifndef::doctype-manpage[]
* <<XR_REFERENCE_SPACE_TYPE_STAGE,ename:XR_REFERENCE_SPACE_TYPE_STAGE>>.
endif::doctype-manpage[]
  The `STAGE` reference space is a runtime-defined flat, rectangular space
  that is empty and can be walked around on.
  The origin is on the floor at the center of the rectangle, with +Y up, and
  the X and Z axes aligned with the rectangle edges.
  The runtime may: not be able to locate spaces relative to the `STAGE`
  reference space if the user has not yet defined one within the
  runtime-specific UI.
  Applications can: use flink:xrGetReferenceSpaceBoundsRect to determine the
  extents of the `STAGE` reference space's XZ bounds rectangle, if defined.
+
Support for the `STAGE` reference space is optional:.

ifdef::doctype-manpage[]
* xref:XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR.adoc[ename:XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR].
endif::doctype-manpage[]
ifndef::doctype-manpage[]
* <<XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR,ename:XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR>>
endif::doctype-manpage[]
  (provided by `XR_VERSION_1_1`) Similar to {local_space}, the `LOCAL_FLOOR`
  reference space establishes a world-locked origin, gravity-aligned to
  exclude pitch and roll, with +Y up, +X to the right, and -Z forward.
  However, the origin of this space is defined to be on an estimate of the
  floor level.
+
Runtimes must: support `LOCAL_FLOOR` reference space.

****

An slink:XrSpace handle for a reference space is created using
flink:xrCreateReferenceSpace, by specifying the chosen reference space type
and a pose within the natural reference frame defined for that reference
space type.

Runtimes implement well-known reference spaces from
elink:XrReferenceSpaceType if they support tracking of that kind.
Available reference space types are indicated by
flink:xrEnumerateReferenceSpaces.
Note that other spaces can be created as well, such as pose action spaces
created by flink:xrCreateActionSpace, which are not enumerated by that API.

--

==== View Reference Space
[open,refpage='XR_REFERENCE_SPACE_TYPE_VIEW',desc='View reference space',type='freeform',xrefs='XrReferenceSpaceType']
--
[[XR_REFERENCE_SPACE_TYPE_VIEW]]

The ename:XR_REFERENCE_SPACE_TYPE_VIEW or `VIEW` reference space tracks the
view origin used to generate view transforms for the primary viewer (or
centroid of view origins if stereo), with +Y up, +X to the right, and -Z
forward.
This space points in the forward direction for the viewer without
incorporating the user's eye orientation, and is not gravity-aligned.

The {view_space} is primarily useful when projecting from the user's
perspective into another space to obtain a targeting ray, or when rendering
small head-locked content such as a reticle.
Content rendered in the {view_space} will stay at a fixed point on
head-mounted displays and may be uncomfortable to view if too large.
To obtain the ideal view and projection transforms to use each frame for
rendering world content, applications should call flink:xrLocateViews
instead of using this space.

--


==== Local Reference Space
[open,refpage='XR_REFERENCE_SPACE_TYPE_LOCAL',desc='Local reference space',type='freeform',xrefs='XrReferenceSpaceType']
--
[[XR_REFERENCE_SPACE_TYPE_LOCAL]]

The ename:XR_REFERENCE_SPACE_TYPE_LOCAL or `LOCAL` reference space
establishes a world-locked origin, gravity-aligned to exclude pitch and
roll, with +Y up, +X to the right, and -Z forward.
This space locks in both its initial position and orientation, which the
runtime may: define to be either the initial position at application launch
or some other calibrated zero position.

When a user needs to recenter the {local_space}, a runtime may: offer some
system-level recentering interaction that is transparent to the application,
but which causes the current leveled head space to become the new `LOCAL`
space.
When such a recentering occurs, the runtime must: queue the
slink:XrEventDataReferenceSpaceChangePending event, with the recentered
`LOCAL` space origin only taking effect for flink:xrLocateSpace or
flink:xrLocateViews calls whose basetype:XrTime parameter is greater than or
equal to the slink:XrEventDataReferenceSpaceChangePending::pname:changeTime
in that event.

When views, controllers or other spaces experience tracking loss relative to
the {local_space}, runtimes should: continue to provide inferred or
last-known pname:position and pname:orientation values.
These inferred poses can, for example, be based on neck model updates,
inertial dead reckoning, or a last-known position, so long as it is still
reasonable for the application to use that pose.
While a runtime is providing position data, it must: continue to set
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT and
ename:XR_VIEW_STATE_POSITION_VALID_BIT but it can: clear
ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT and
ename:XR_VIEW_STATE_POSITION_TRACKED_BIT to indicate that the position is
inferred or last-known in this way.

When tracking is recovered, runtimes should: snap the pose of other spaces
back into position relative to the original origin of {local_space}.

--


==== Stage Reference Space
[open,refpage='XR_REFERENCE_SPACE_TYPE_STAGE',desc='Stage reference space',type='freeform',xrefs='XrReferenceSpaceType']
--
[[XR_REFERENCE_SPACE_TYPE_STAGE]]

The `STAGE` reference space is a runtime-defined flat, rectangular space
that is empty and can be walked around on.
The origin is on the floor at the center of the rectangle, with +Y up, and
the X and Z axes aligned with the rectangle edges.
The runtime may: not be able to locate spaces relative to the `STAGE`
reference space if the user has not yet defined one within the
runtime-specific UI.
Applications can: use flink:xrGetReferenceSpaceBoundsRect to determine the
extents of the `STAGE` reference space's XZ bounds rectangle, if defined.

The {stage_space} is useful when an application needs to render
*standing-scale* content (no bounds) or *room-scale* content (with bounds)
that is relative to the physical floor.

When the user redefines the origin or bounds of the current {stage_space},
or the runtime otherwise switches to a new `STAGE` space definition, the
runtime must: queue the slink:XrEventDataReferenceSpaceChangePending event,
with the new `STAGE` space origin only taking effect for flink:xrLocateSpace
or flink:xrLocateViews calls whose basetype:XrTime parameter is greater than
or equal to the
slink:XrEventDataReferenceSpaceChangePending::pname:changeTime in that
event.

When views, controllers, or other spaces experience tracking loss relative
to the {stage_space}, runtimes should: continue to provide inferred or
last-known pname:position and pname:orientation values.
These inferred poses can, for example, be based on neck model updates,
inertial dead reckoning, or a last-known position, so long as it is still
reasonable for the application to use that pose.
While a runtime is providing position data, it must: continue to set
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT and
ename:XR_VIEW_STATE_POSITION_VALID_BIT but it can: clear
ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT and
ename:XR_VIEW_STATE_POSITION_TRACKED_BIT to indicate that the position is
inferred or last-known in this way.
When tracking is recovered, runtimes should: snap the pose of other spaces
back into position relative to the original origin of the {stage_space}.

--

==== Local Floor Reference Space
[open,refpage='XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR',desc='Local floor reference space',type='freeform',xrefs='XrReferenceSpaceType']
--
[[XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR]]

Local floor reference space, indicated by
ename:XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, is closely related to the `LOCAL`
reference space.
It always aligns with the {local_space}, and matches it in X and Z position.
However, unlike the `LOCAL` space, the `LOCAL_FLOOR` space has its Y axis
origin on the runtime's best estimate of the floor level under the origin of
the `LOCAL` space.

The location of the origin of the `LOCAL_FLOOR` space must: match the
{local_space} in the X and Z coordinates but not in the Y coordinate.

The orientation of the `LOCAL_FLOOR` space must: match the {local_space}.

The runtime must: establish the Y axis origin at its best estimate of the
floor level under the origin of the {local_space} space, subject to
requirements under the following conditions to match the floor level of the
{stage_space}.

If all of the following conditions are true, the Y axis origin of the
{local_floor_space} must: match the Y axis origin of the {stage_space}:

* the {stage_space} is supported
* the location of the {local_space} relative to the `STAGE` space has valid
  position (ename:XR_SPACE_LOCATION_POSITION_VALID_BIT is set)
* bounds are available from flink:xrGetReferenceSpaceBoundsRect for the
  `STAGE` space
* the position of the `LOCAL` space relative to the `STAGE` space is within
  the `STAGE` space XZ bounds

That is, if there is a stage with bounds, and if the local space and thus
the local floor is logically within the stage, the local floor and the stage
share the same floor level.

When the origin of the {local_space} is changed in orientation or XZ
position, the origin of the {local_floor_space} must: also change
accordingly.

When a change in origin of the {local_floor_space} occurs, the runtime must:
queue the slink:XrEventDataReferenceSpaceChangePending event, with the
changed `LOCAL_FLOOR` space origin only taking effect for
flink:xrLocateSpace or flink:xrLocateViews calls whose basetype:XrTime
parameter is greater than or equal to the
slink:XrEventDataReferenceSpaceChangePending::pname:changeTime in that
event.
--

[open,refpage='xrGetReferenceSpaceBoundsRect',desc='Gets the bounds rectangle of a reference space',type='protos',xrefs='xrCreateReferenceSpace']
--
The flink:xrGetReferenceSpaceBoundsRect function is defined as:

include::{generated}/api/protos/xrGetReferenceSpaceBoundsRect.adoc[]

.Parameter Descriptions
****
* pname:session is a handle to an slink:XrSession previously created with
  flink:xrCreateSession.
* pname:referenceSpaceType is the reference space type whose bounds should
  be retrieved.
* pname:bounds is the returned space extents.
****

XR systems may: have limited real world spatial ranges in which users can
freely move around while remaining tracked.
Applications sometimes wish to query these boundaries and alter application
behavior or content placement to ensure the user can complete the experience
while remaining within the boundary.
Applications can: query this information using
flink:xrGetReferenceSpaceBoundsRect.

When called, flink:xrGetReferenceSpaceBoundsRect should: return the extents
of a rectangle that is clear of obstacles down to the floor, allowing where
the user can freely move while remaining tracked, if available for that
reference space.
The returned extent represents the dimensions of an axis-aligned bounding
box where the slink:XrExtent2Df::pname:width and
slink:XrExtent2Df::pname:height fields correspond to the X and Z axes of the
provided space, with the extents centered at the origin of the space.
Not all systems or spaces support boundaries.
If a runtime is unable to provide bounds for a given space,
ename:XR_SPACE_BOUNDS_UNAVAILABLE must: be returned and all fields of
pname:bounds must: be set to 0.

The returned extents are expressed relative to the natural origin of the
provided elink:XrReferenceSpaceType and must: not incorporate any origin
offsets specified by the application during calls to
flink:xrCreateReferenceSpace.

The runtime must: return ename:XR_ERROR_REFERENCE_SPACE_UNSUPPORTED if the
elink:XrReferenceSpaceType passed in pname:referenceSpaceType is not
supported by this pname:session.

When a runtime will begin operating with updated space bounds, the runtime
must: queue a corresponding slink:XrEventDataReferenceSpaceChangePending
event.

include::{generated}/validity/protos/xrGetReferenceSpaceBoundsRect.adoc[]
--

[open,refpage='XrEventDataReferenceSpaceChangePending',desc='Notifies the application that a reference space is changing',type='structs',xrefs='xrCreateReferenceSpace XrReferenceSpaceType']
--
The slink:XrEventDataReferenceSpaceChangePending event structure is defined
as:

include::{generated}/api/structs/XrEventDataReferenceSpaceChangePending.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
  No such structures are defined in core OpenXR.
* pname:session is the slink:XrSession for which the reference space is
  changing.
* pname:referenceSpaceType is the elink:XrReferenceSpaceType that is
  changing.
* pname:changeTime is the target basetype:XrTime after which
  flink:xrLocateSpace or flink:xrLocateViews will return values that respect
  this change.
* pname:poseValid is true if the runtime can determine the
  pname:poseInPreviousSpace of the new space in the previous space before
  the change.
* pname:poseInPreviousSpace is an slink:XrPosef defining the position and
  orientation of the new reference space's natural origin within the natural
  reference frame of its previous space.
****

The slink:XrEventDataReferenceSpaceChangePending event is sent to the
application to notify it that the origin (and perhaps the bounds) of a
reference space is changing.
This may occur due to the user recentering the space explicitly, or the
runtime otherwise switching to a different space definition.

The reference space change must: only take effect for flink:xrLocateSpace or
flink:xrLocateViews calls whose basetype:XrTime parameter is greater than or
equal to the pname:changeTime provided in that event.
Runtimes should: provide a pname:changeTime to applications that allows for
a deep render pipeline to present frames that are already in flight using
the previous definition of the space.
Runtimes should: choose a pname:changeTime that is midway between the
slink:XrFrameState::pname:predictedDisplayTime of future frames to avoid
threshold issues with applications that calculate future frame times using
slink:XrFrameState::pname:predictedDisplayPeriod.

The pname:poseInPreviousSpace provided here must: only describe the change
in the natural origin of the reference space and must: not incorporate any
origin offsets specified by the application during calls to
flink:xrCreateReferenceSpace.
If the runtime does not know the location of the space's new origin relative
to its previous origin, pname:poseValid must: be false, and the position and
orientation of pname:poseInPreviousSpace are undefined.
include::{generated}/validity/structs/XrEventDataReferenceSpaceChangePending.adoc[]
--

[[spaces-action-spaces]]
=== Action Spaces
An slink:XrSpace handle for a pose action is created using
flink:xrCreateActionSpace, by specifying the chosen pose action and a pose
within the action's natural reference frame.

Runtimes support suggested pose action bindings to well-known user paths
with subpathname:/pose subpaths if they support tracking for that particular
identifier.

Some example well-known pose action paths:
****
* pathname:/user/hand/left/input/grip
* pathname:/user/hand/left/input/aim
* pathname:/user/hand/right/input/grip
* pathname:/user/hand/right/input/aim
****

For definitions of these well-known pose device paths, see the discussion of
<<semantic-paths-input,device input subpaths>> in the Semantic Paths
chapter.

[[spaces-action-spaces-lifetime]]
==== Action Spaces Lifetime
slink:XrSpace handles created for a pose action must: be unlocatable unless
the action set that contains the corresponding pose action was set as active
via the most recent flink:xrSyncActions call.
If the underlying device that is active for the action changes, the device
this space is tracking must: only change to track the new device when
flink:xrSyncActions is called.

If flink:xrLocateSpace is called with an unlocatable action space, the
implementation must: return no position or orientation and both
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT and
ename:XR_SPACE_LOCATION_ORIENTATION_VALID_BIT must: be unset.
If slink:XrSpaceVelocity is also supplied,
ename:XR_SPACE_VELOCITY_LINEAR_VALID_BIT and
ename:XR_SPACE_VELOCITY_ANGULAR_VALID_BIT must: be unset.
If flink:xrLocateViews is called with an unlocatable action space, the
implementation must: return no position or orientation and both
ename:XR_VIEW_STATE_POSITION_VALID_BIT and
ename:XR_VIEW_STATE_ORIENTATION_VALID_BIT must: be unset.

[[spaces-lifecycle]]
=== Space Lifecycle

There are a small set of core APIs that allow applications to reason about
reference spaces, action spaces, and their relative locations.

==== xrEnumerateReferenceSpaces

[open,refpage='xrEnumerateReferenceSpaces',desc='Enumerate available reference spaces',type='protos',xrefs='XrSpace']
--
The flink:xrEnumerateReferenceSpaces function is defined as:

include::{generated}/api/protos/xrEnumerateReferenceSpaces.adoc[]

.Parameter Descriptions
****
* pname:session is a handle to an slink:XrSession previously created with
  flink:xrCreateSession.
* pname:spaceCapacityInput is the capacity of the pname:spaces array, or 0
  to indicate a request to retrieve the required capacity.
* pname:spaceCountOutput is a pointer to the count of pname:spaces written,
  or a pointer to the required capacity in the case that
  pname:spaceCapacityInput is insufficient.
* pname:spaces is a pointer to an application-allocated array that will be
  filled with the enumerant of each supported reference space.
  It can: be code:NULL if pname:spaceCapacityInput is 0.
* See the <<fundamentals-buffer-size-parameters>> section for a detailed
  description of retrieving the required pname:spaces size.
****

Enumerates the set of reference space types that this runtime supports for a
given session.
Runtimes must: always return identical buffer contents from this enumeration
for the lifetime of the session.

If a session enumerates support for a given reference space type, calls to
flink:xrCreateReferenceSpace must: succeed for that session, with any
transient unavailability of poses expressed later during calls to
flink:xrLocateSpace.

include::{generated}/validity/protos/xrEnumerateReferenceSpaces.adoc[]
--

==== xrCreateReferenceSpace

[open,refpage='xrCreateReferenceSpace',desc='Creates a reference space',type='protos',xrefs='XrSpace XrReferenceSpaceCreateInfo xrDestroySpace']
--
The flink:xrCreateReferenceSpace function is defined as:

include::{generated}/api/protos/xrCreateReferenceSpace.adoc[]

.Parameter Descriptions
****
* pname:session is a handle to an slink:XrSession previously created with
  flink:xrCreateSession.
* pname:createInfo is the slink:XrReferenceSpaceCreateInfo used to specify
  the space.
* pname:space is the returned space handle.
****

Creates an slink:XrSpace handle based on a chosen reference space.
Application can: provide an slink:XrPosef to define the position and
orientation of the new space's origin within the natural reference frame of
the reference space.

Multiple slink:XrSpace handles may exist simultaneously, up to some limit
imposed by the runtime.
The slink:XrSpace handle must: be eventually freed via the
flink:xrDestroySpace function.

The runtime must: return ename:XR_ERROR_REFERENCE_SPACE_UNSUPPORTED if the
given reference space type is not supported by this pname:session.

include::{generated}/validity/protos/xrCreateReferenceSpace.adoc[]
--

[open,refpage='XrReferenceSpaceCreateInfo',desc='Creation info for a reference space',type='structs',xrefs='XrSpace xrCreateReferenceSpace']
--
The slink:XrReferenceSpaceCreateInfo structure is defined as:

include::{generated}/api/structs/XrReferenceSpaceCreateInfo.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
  No such structures are defined in core OpenXR.
* pname:referenceSpaceType is the chosen elink:XrReferenceSpaceType.
* pname:poseInReferenceSpace is an slink:XrPosef defining the position and
  orientation of the new space's origin within the natural reference frame
  of the reference space.
****

include::{generated}/validity/structs/XrReferenceSpaceCreateInfo.adoc[]
--

==== xrCreateActionSpace
[open,refpage='xrCreateActionSpace',desc='Creates a space based on a pose action',type='protos',xrefs='XrSpace XrActionSpaceCreateInfo xrDestroySpace']
--
The flink:xrCreateActionSpace function is defined as:

include::{generated}/api/protos/xrCreateActionSpace.adoc[]

.Parameter Descriptions
****
* pname:session is the slink:XrSession to create the action space in.
* pname:createInfo is the slink:XrActionSpaceCreateInfo used to specify the
  space.
* pname:space is the returned space handle.
****

Creates an slink:XrSpace handle based on a chosen pose action.
Application can: provide an slink:XrPosef to define the position and
orientation of the new space's origin within the natural reference frame of
the action space.

Multiple slink:XrSpace handles may exist simultaneously, up to some limit
imposed by the runtime.
The slink:XrSpace handle must be eventually freed via the
flink:xrDestroySpace function or by destroying the parent slink:XrAction
handle.

The runtime must: return ename:XR_ERROR_ACTION_TYPE_MISMATCH if the action
provided in slink:XrActionSpaceCreateInfo::pname:action is not of type
ename:XR_ACTION_TYPE_POSE_INPUT.

include::{generated}/validity/protos/xrCreateActionSpace.adoc[]
--

[open,refpage='XrActionSpaceCreateInfo',desc='Creation info for an action space',type='structs',xrefs='XrSpace xrCreateActionSpace']
--
The slink:XrActionSpaceCreateInfo structure is defined as:

include::{generated}/api/structs/XrActionSpaceCreateInfo.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
  No such structures are defined in core OpenXR.
* pname:action is a handle to a pose slink:XrAction previously created with
  flink:xrCreateAction.
* pname:subactionPath is dlink:XR_NULL_PATH or an basetype:XrPath that was
  specified when the action was created.
  If pname:subactionPath is a valid path not specified when the action was
  created the runtime must: return ename:XR_ERROR_PATH_UNSUPPORTED.
  If this parameter is set, the runtime must: create a space that is
  relative to only that subaction's pose binding.
* pname:poseInActionSpace is an slink:XrPosef defining the position and
  orientation of the new space's origin within the natural reference frame
  of the pose action.
****

include::{generated}/validity/structs/XrActionSpaceCreateInfo.adoc[]
--

==== xrDestroySpace

[open,refpage='xrDestroySpace',desc='Destroys an XrSpace',type='protos',xrefs='XrSpace xrCreateReferenceSpace xrCreateActionSpace']
--
The flink:xrDestroySpace function is defined as:

include::{generated}/api/protos/xrDestroySpace.adoc[]

.Parameter Descriptions
****
* pname:space is a handle to an slink:XrSpace previously created by a
  function such as flink:xrCreateReferenceSpace.
****

slink:XrSpace handles are destroyed using flink:xrDestroySpace.
The runtime may: still use this space if there are active dependencies (e.g,
compositions in progress).

include::{generated}/validity/protos/xrDestroySpace.adoc[]
--

[[spaces-locating-spaces]]
=== Locating Spaces

Applications use the flink:xrLocateSpace function to find the pose of an
slink:XrSpace's origin within a base slink:XrSpace at a given historical or
predicted time.
If an application wants to know the velocity of the space's origin, it can:
chain an slink:XrSpaceVelocity structure to the pname:next pointer of the
slink:XrSpaceLocation structure when calling the flink:xrLocateSpace
function.
Applications should: inspect the output elink:XrSpaceLocationFlagBits and
elink:XrSpaceVelocityFlagBits to determine the validity and tracking status
of the components of the location.

==== xrLocateSpace

[open,refpage='xrLocateSpace',desc='Locates a space with reference to another space',type='protos',xrefs='XrSpace XrSpaceLocationFlags']
--
flink:xrLocateSpace provides the physical location of a space in a base
space at a specified time, if currently known by the runtime.

include::{generated}/api/protos/xrLocateSpace.adoc[]

.Parameter Descriptions
****
* pname:space identifies the target space to locate.
* pname:baseSpace identifies the underlying space in which to locate
  pname:space.
* pname:time is the time for which the location should be provided.
* pname:location provides the location of pname:space in pname:baseSpace.
****

For a pname:time in the past, the runtime should: locate the spaces based on
the runtime's most accurate current understanding of how the world was at
that historical time.

For a pname:time in the future, the runtime should: locate the spaces based
on the runtime's most up-to-date prediction of how the world will be at that
future time.

The minimum valid range of values for pname:time are described in
<<fundamentals-prediction-time-limits>>.
For values of pname:time outside this range, flink:xrLocateSpace may: return
a location with no position and ename:XR_SPACE_LOCATION_POSITION_VALID_BIT
unset.

Some devices improve their understanding of the world as the device is used.
The location returned by flink:xrLocateSpace for a given pname:space,
pname:baseSpace and pname:time may: change over time, even for spaces that
track static objects, as one or both spaces adjust their origins.

During tracking loss of pname:space relative to pname:baseSpace, runtimes
should: continue to provide inferred or last-known
slink:XrPosef::pname:position and slink:XrPosef::pname:orientation values.
These inferred poses can, for example, be based on neck model updates,
inertial dead reckoning, or a last-known position, so long as it is still
reasonable for the application to use that pose.
While a runtime is providing position data, it must: continue to set
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT but it can: clear
ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT to indicate that the position
is inferred or last-known in this way.

If the runtime has not yet observed even a last-known pose for how to locate
pname:space in pname:baseSpace (e.g. one space is an action space bound to a
motion controller that has not yet been detected, or the two spaces are in
disconnected fragments of the runtime's tracked volume), the runtime should:
return a location with no position and
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT unset.

The runtime must: return a location with both
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT and
ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT set when locating pname:space
and pname:baseSpace if both spaces were created relative to the same entity
(e.g. two action spaces for the same action), even if the entity is
currently untracked.
The location in this case is the difference in the two spaces'
application-specified transforms relative to that common entity.

During tracking loss, the runtime should: return a location with
ename:XR_SPACE_LOCATION_POSITION_VALID_BIT and
ename:XR_SPACE_LOCATION_ORIENTATION_VALID_BIT set and
ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT and
ename:XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT unset for spaces tracking
two static entities in the world when their relative pose is known to the
runtime.
This enables applications to continue to make use of the runtime's latest
knowledge of the world.

If an slink:XrSpaceVelocity structure is chained to the
slink:XrSpaceLocation::pname:next pointer, and the velocity is observed or
can be calculated by the runtime, the runtime must: fill in the linear
velocity of the origin of space within the reference frame of
pname:baseSpace and set the ename:XR_SPACE_VELOCITY_LINEAR_VALID_BIT.
Similarly, if an slink:XrSpaceVelocity structure is chained to the
slink:XrSpaceLocation::pname:next pointer, and the angular velocity is
observed or can be calculated by the runtime, the runtime must: fill in the
angular velocity of the origin of space within the reference frame of
pname:baseSpace and set the ename:XR_SPACE_VELOCITY_ANGULAR_VALID_BIT.

[[spaces-example-locate-code]]
The following example code shows how an application can get both the
location and velocity of a space within a base space using the
flink:xrLocateSpace function by chaining an slink:XrSpaceVelocity to the
`next` pointer of slink:XrSpaceLocation and calling flink:xrLocateSpace.

[source,C++]
----
XrSpace space;      // previously initialized
XrSpace baseSpace;  // previously initialized
XrTime time;        // previously initialized

XrSpaceVelocity velocity {XR_TYPE_SPACE_VELOCITY};
XrSpaceLocation location {XR_TYPE_SPACE_LOCATION, &velocity};
xrLocateSpace(space, baseSpace, time, &location);
----

include::{generated}/validity/protos/xrLocateSpace.adoc[]
--


[open,refpage='XrSpaceLocation',desc='Contains info about a space's location',type='structs',xrefs='XrSpace xrLocateSpace XrSpaceLocationFlags XrSpaceVelocity']
--
The slink:XrSpaceLocation structure is defined as:

include::{generated}/api/structs/XrSpaceLocation.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain, such as slink:XrSpaceVelocity.
* pname:locationFlags is a bitfield, with bit masks defined in
  elink:XrSpaceLocationFlagBits, to indicate which members contain valid
  data.
  If none of the bits are set, no other fields in this structure should: be
  considered to be valid or meaningful.
* pname:pose is an slink:XrPosef defining the position and orientation of
  the origin of flink:xrLocateSpace::pname:space within the reference frame
  of flink:xrLocateSpace::pname:baseSpace.
****

include::{generated}/validity/structs/XrSpaceLocation.adoc[]
--

[open,refpage='XrSpaceLocationFlags',desc='Space location flags',type='flags',xrefs='xrLocateSpace XrSpaceLocation XrSpaceLocationFlagBits']
--
The slink:XrSpaceLocation::pname:locationFlags member is of the following
type, and contains a bitwise-OR of zero or more of the bits defined in
elink:XrSpaceLocationFlagBits.

include::{generated}/api/flags/XrSpaceLocationFlags.adoc[]
--

[open,refpage='XrSpaceLocationFlagBits',desc='Space location flags',type='enums',xrefs='xrLocateSpace XrSpaceLocation XrSpaceLocationFlags']
--
Valid bits for elink:XrSpaceLocationFlags are defined by
elink:XrSpaceLocationFlagBits, which is specified as:

include::{generated}/api/enums/XrSpaceLocationFlagBits.adoc[]

The flag bits have the following meanings:

.Flag Descriptions
****
* ename:XR_SPACE_LOCATION_ORIENTATION_VALID_BIT indicates that the
  pname:pose field's pname:orientation field contains valid data.
  For a space location tracking a device with its own inertial tracking,
  ename:XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT should: remain set when
  this bit is set.
  Applications must: not read the pname:pose field's pname:orientation if
  this flag is unset.
* ename:XR_SPACE_LOCATION_POSITION_VALID_BIT indicates that the pname:pose
  field's pname:position field contains valid data.
  When a space location loses tracking, runtimes should: continue to provide
  valid but untracked pname:position values that are inferred or last-known,
  so long as it's still meaningful for the application to use that position,
  clearing ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT until positional
  tracking is recovered.
  Applications must: not read the pname:pose field's pname:position if this
  flag is unset.
* ename:XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT indicates that the
  pname:pose field's pname:orientation field represents an actively tracked
  orientation.
  For a space location tracking a device with its own inertial tracking,
  this bit should: remain set when
  ename:XR_SPACE_LOCATION_ORIENTATION_VALID_BIT is set.
  For a space location tracking an object whose orientation is no longer
  known during tracking loss (e.g. an observed QR code), runtimes should:
  continue to provide valid but untracked pname:orientation values, so long
  as it's still meaningful for the application to use that orientation.
* ename:XR_SPACE_LOCATION_POSITION_TRACKED_BIT indicates that the pname:pose
  field's pname:position field represents an actively tracked position.
  When a space location loses tracking, runtimes should: continue to provide
  valid but untracked pname:position values that are inferred or last-known,
  e.g. based on neck model updates, inertial dead reckoning, or a last-known
  position, so long as it's still meaningful for the application to use that
  position.
****
--


[open,refpage='XrSpaceVelocity',desc='Contains info about a space's linear and angular velocity',type='structs',xrefs='XrSpaceLocation XrSpace xrLocateSpace XrSpaceVelocityFlags']
--

The slink:XrSpaceVelocity structure is defined as:

include::{generated}/api/structs/XrSpaceVelocity.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
  No such structures are defined in core OpenXR.
* pname:velocityFlags is a bitfield, with bit masks defined in
  elink:XrSpaceVelocityFlagBits, to indicate which members contain valid
  data.
  If none of the bits are set, no other fields in this structure should: be
  considered to be valid or meaningful.
* pname:linearVelocity is the relative linear velocity of the origin of
  flink:xrLocateSpace::pname:space with respect to and expressed in the
  reference frame of flink:xrLocateSpace::pname:baseSpace, in units of
  meters per second.
* pname:angularVelocity is the relative angular velocity of
  flink:xrLocateSpace::pname:space with respect to
  flink:xrLocateSpace::pname:baseSpace.
  The vector's direction is expressed in the reference frame of
  flink:xrLocateSpace::pname:baseSpace and is parallel to the rotational
  axis of flink:xrLocateSpace::pname:space.
  The vector's magnitude is the relative angular speed of
  flink:xrLocateSpace::pname:space in radians per second.
  The vector follows the right-hand rule for torque/rotation.
****

include::{generated}/validity/structs/XrSpaceVelocity.adoc[]
--


[open,refpage='XrSpaceVelocityFlags',desc='Space velocity flags',type='flags',xrefs='XrSpaceVelocity XrSpaceVelocityFlagBits']
--
The slink:XrSpaceVelocity::pname:velocityFlags member is of the following
type, and contains a bitwise-OR of zero or more of the bits defined in
elink:XrSpaceVelocityFlagBits.

include::{generated}/api/flags/XrSpaceVelocityFlags.adoc[]
--

[open,refpage='XrSpaceVelocityFlagBits',desc='Space velocity flags',type='enums',xrefs='XrSpaceVelocity XrSpaceVelocityFlags']
--
Valid bits for elink:XrSpaceVelocityFlags are defined by
elink:XrSpaceVelocityFlagBits, which is specified as:

include::{generated}/api/enums/XrSpaceVelocityFlagBits.adoc[]

The flag bits have the following meanings:

include::{generated}/api/enums/XrSpaceVelocityFlagBits.comments.adoc[]

--


==== Locate spaces

[open,refpage='xrLocateSpaces',type='protos',desc='Locate an array of spaces',xrefs='XrSpacesLocateInfo XrSpaceLocations']
--
Applications can: use flink:xrLocateSpaces function to locate an array of
spaces.

The flink:xrLocateSpaces function is defined as:

include::{generated}/api/protos/xrLocateSpaces.adoc[]

.Parameter Descriptions
****
* pname:session is an slink:XrSession handle previously created with
  flink:xrCreateSession.
* pname:locateInfo is a pointer to an slink:XrSpacesLocateInfo that provides
  the input information to locate spaces.
* pname:spaceLocations is a pointer to an slink:XrSpaceLocations for the
  runtime to return the locations of the specified spaces in the base space.
****

flink:xrLocateSpaces provides the physical location of one or more spaces in
a base space at a specified time, if currently known by the runtime.

The slink:XrSpacesLocateInfo::pname:time, the
slink:XrSpacesLocateInfo::pname:baseSpace, and each space in
slink:XrSpacesLocateInfo::pname:spaces, in the pname:locateInfo parameter,
all follow the same specifics as the corresponding inputs to the
flink:xrLocateSpace function.

include::{generated}/validity/protos/xrLocateSpaces.adoc[]
--

[open,refpage='XrSpacesLocateInfo',type='structs',desc='Inputs the information to locate spaces.',xrefs='xrLocateSpaces']
--
The slink:XrSpacesLocateInfo structure is defined as:

include::{generated}/api/structs/XrSpacesLocateInfo.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
  No such structures are defined in core OpenXR or this extension.
* pname:baseSpace identifies the underlying space in which to locate
  pname:spaces.
* pname:time is the time for which the location is requested.
* pname:spaceCount is a code:uint32_t specifying the count of elements in
  the pname:spaces array.
* pname:spaces is an array of valid slink:XrSpace handles to be located.
****

The pname:time, the pname:baseSpace, and each space in pname:spaces all
follow the same specifics as the corresponding inputs to the
flink:xrLocateSpace function.

The pname:baseSpace and all of the slink:XrSpace handles in the pname:spaces
array must: be valid and share the same parent slink:XrSession.

If the pname:time is invalid, the flink:xrLocateSpaces must: return
ename:XR_ERROR_TIME_INVALID.

The pname:spaceCount must: be a positive number, i.e. the array pname:spaces
must: not be empty.
Otherwise, the runtime must: return ename:XR_ERROR_VALIDATION_FAILURE.

include::{generated}/validity/structs/XrSpacesLocateInfo.adoc[]
--

[open,refpage='XrSpaceLocations',type='structs',desc='Returns an array of space locations',xrefs='xrLocateSpaces']
--
The slink:XrSpaceLocations structure is defined as:

include::{generated}/api/structs/XrSpaceLocations.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain, such as slink:XrSpaceVelocities.
* pname:locationCount is a code:uint32_t specifying the count of elements in
  the pname:locations array.
* pname:locations is an array of slink:XrSpaceLocations for the runtime to
  populate with the locations of the specified spaces in the
  slink:XrSpacesLocateInfo::pname:baseSpace at the specified
  slink:XrSpacesLocateInfo::pname:time.
****

The slink:XrSpaceLocations structure contains an array of space locations in
the member pname:locations, to be used as output for flink:xrLocateSpaces.
The application must: allocate this array to be populated with the function
output.
The pname:locationCount value must: be the same as
slink:XrSpacesLocateInfo::pname:spaceCount, otherwise, the
flink:xrLocateSpaces function must: return
ename:XR_ERROR_VALIDATION_FAILURE.

include::{generated}/validity/structs/XrSpaceLocations.adoc[]
--

[open,refpage='XrSpaceLocationData',type='structs',desc='Returns the data of a space's location',xrefs='XrSpaceLocations']
--
The slink:XrSpaceLocationData structure is defined as:

include::{generated}/api/structs/XrSpaceLocationData.adoc[]

.Member Descriptions
****
* pname:locationFlags is a bitfield, with bit masks defined in
  elink:XrSpaceLocationFlagBits.
  It behaves the same as slink:XrSpaceLocation::pname:locationFlags.
* pname:pose is an slink:XrPosef that behaves the same as
  slink:XrSpaceLocation::pname:pose.
****

This is a single element of the array in
slink:XrSpaceLocations::pname:locations, and is used to return the pose and
location flags for a single space with respect to the specified base space
from a call to flink:xrLocateSpaces.
It does not accept chained structures to allow for easier use in dynamically
allocated container datatypes.
Chained structures are possible with the slink:XrSpaceLocations that
describes an array of these elements.

include::{generated}/validity/structs/XrSpaceLocationData.adoc[]
--

==== Locate space velocities

[open,refpage='XrSpaceVelocities',type='structs',desc='Returns an array of space velocities',xrefs='xrLocateSpaces XrSpaceLocations']
--
Applications can: request the velocities of spaces by chaining the
slink:XrSpaceVelocities structure to the next pointer of
slink:XrSpaceLocations when calling flink:xrLocateSpaces.

The slink:XrSpaceVelocities structure is defined as:

include::{generated}/api/structs/XrSpaceVelocities.adoc[]

.Member Descriptions
****
* pname:type is the elink:XrStructureType of this structure.
* pname:next is code:NULL or a pointer to the next structure in a structure
  chain.
* pname:velocityCount is a code:uint32_t specifying the count of elements in
  the pname:velocities array.
* pname:velocities is an array of slink:XrSpaceVelocityData for the runtime
  to populate with the velocities of the specified spaces in the
  slink:XrSpacesLocateInfo::pname:baseSpace at the specified
  slink:XrSpacesLocateInfo::pname:time.
****

The pname:velocities member contains an array of space velocities in the
member pname:velocities, to be used as output for flink:xrLocateSpaces.
The application must: allocate this array to be populated with the function
output.
The pname:velocityCount value must: be the same as
slink:XrSpacesLocateInfo::pname:spaceCount, otherwise, the
flink:xrLocateSpaces function must: return
ename:XR_ERROR_VALIDATION_FAILURE.

include::{generated}/validity/structs/XrSpaceVelocities.adoc[]
--

[open,refpage='XrSpaceVelocityData',type='structs',desc='Returns the data of a space's velocity',xrefs='XrSpaceVelocities']
--
The slink:XrSpaceVelocityData structure is defined as:

include::{generated}/api/structs/XrSpaceVelocityData.adoc[]

.Member Descriptions
****
* pname:velocityFlags is a bitfield, with bit values defined in
  elink:XrSpaceVelocityFlagBits.
  It behaves the same as slink:XrSpaceVelocity::pname:velocityFlags.
* pname:linearVelocity is an slink:XrVector3f.
  It behaves the same as slink:XrSpaceVelocity::pname:linearVelocity.
* pname:angularVelocity is an slink:XrVector3f.
  It behaves the same as slink:XrSpaceVelocity::pname:angularVelocity.
****

This is a single element of the array in
slink:XrSpaceVelocities::pname:velocities, and is used to return the linear
and angular velocity and velocity flags for a single space with respect to
the specified base space from a call to flink:xrLocateSpaces.
It does not accept chained structures to allow for easier use in dynamically
allocated container datatypes.

include::{generated}/validity/structs/XrSpaceVelocityData.adoc[]
--

==== Example code for flink:xrLocateSpaces

The following example code shows how an application retrieves both the
location and velocity of one or more spaces in a base space at a given time
using the flink:xrLocateSpaces function.

[source,C++]
----
XrInstance instance; // previously initialized
XrSession session; // previously initialized
XrSpace baseSpace; // previously initialized
std::vector<XrSpace> spacesToLocate;  // previously initialized

// Prepare output buffers to receive data and get reused in frame loop.
std::vector<XrSpaceLocationData> locationBuffer(spacesToLocate.size());
std::vector<XrSpaceVelocityData> velocityBuffer(spacesToLocate.size());

// Get function pointer for xrLocateSpaces.
PFN_xrLocateSpaces xrLocateSpaces;
CHK_XR(xrGetInstanceProcAddr(instance, "xrLocateSpaces",
                             reinterpret_cast<PFN_xrVoidFunction*>(
                             &xrLocateSpaces)));

// application frame loop
while (1) {
    // Typically the time is the predicted display time returned from xrWaitFrame.
    XrTime displayTime; // previously initialized.

    XrSpacesLocateInfo locateInfo{XR_TYPE_SPACES_LOCATE_INFO};
    locateInfo.baseSpace = baseSpace;
    locateInfo.time = displayTime;
    locateInfo.spaceCount = (uint32_t)spacesToLocate.size();
    locateInfo.spaces = spacesToLocate.data();

    XrSpaceLocations locations{XR_TYPE_SPACE_LOCATIONS};
    locations.locationCount = (uint32_t)locationBuffer.size();
    locations.locations = locationBuffer.data();

    XrSpaceVelocities velocities{XR_TYPE_SPACE_VELOCITIES};
    velocities.velocityCount = (uint32_t)velocityBuffer.size();
    velocities.velocities = velocityBuffer.data();

    locations.next = &velocities;
    CHK_XR(xrLocateSpaces(session, &locateInfo, &locations));

    for (uint32_t i = 0; i < spacesToLocate.size(); i++) {
        const auto positionAndOrientationTracked =
          XR_SPACE_LOCATION_POSITION_TRACKED_BIT | XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT;
        const auto orientationOnlyTracked = XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT;

        if ((locationBuffer[i].locationFlags & positionAndOrientationTracked) == positionAndOrientationTracked) {
            // if the location is 6dof tracked
            do_something(locationBuffer[i].pose.position);
            do_something(locationBuffer[i].pose.orientation);

            const auto velocityValidBits =
              XR_SPACE_VELOCITY_LINEAR_VALID_BIT | XR_SPACE_VELOCITY_ANGULAR_VALID_BIT;
            if ((velocityBuffer[i].velocityFlags & velocityValidBits) == velocityValidBits) {
                do_something(velocityBuffer[i].linearVelocity);
                do_something(velocityBuffer[i].angularVelocity);
            }
        }
        else if ((locationBuffer[i].locationFlags & orientationOnlyTracked) == orientationOnlyTracked) {
            // if the location is 3dof tracked
            do_something(locationBuffer[i].pose.orientation);

            if ((velocityBuffer[i].velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) == XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
                do_something(velocityBuffer[i].angularVelocity);
            }
        }
    }
}
----

