Overview
GuiPlacementHelper is a helper class for placing (dropping) objects from the GUI onto surfaces, using raycasting and physics-based collision testing to find valid placement locations.
Description
The GuiPlacementHelper class provides the ability to snap a held object to a valid placement position on a surface. The update method takes a ray (typically the mouse ray) and a query object ID, and performs a multi-step placement algorithm:
- Raycast: Casts the ray into the scene (after temporarily moving the query object out of the way to avoid self-intersection).
- Surface search: Starting from the ray hit point, searches incrementally away from the surface along the hit normal until the object is no longer in collision (up to 0.5 units away in 0.03-unit increments).
- Gravity settle: From the collision-free position, searches incrementally downward along the gravity direction until the object contacts a surface again (up to 0.1 units in 0.015-unit increments).
- Visual feedback: On success, draws a white circle at the valid placement position. On failure, draws a small red circle at the hit point and hides the object far away.
The maximum placement distance from the ray origin is 2.5 units. The gravity direction defaults to (0, -1, 0) but can be customized.
Usage
Use GuiPlacementHelper in HITL rearrangement tasks where users need to place or drop objects onto surfaces. It provides both the placement logic and visual feedback for valid/invalid placements.
Code Reference
Source Location
Signature
class GuiPlacementHelper:
def __init__(
self,
app_service: AppService,
user_index: int,
gravity_dir: mn.Vector3 = DEFAULT_GRAVITY,
):
...
def update(self, ray: Ray, query_obj_id: int):
...
Import
from habitat_hitl.environment.gui_placement_helper import GuiPlacementHelper
I/O Contract
Inputs
| Name |
Type |
Required |
Description
|
| app_service |
AppService |
Yes |
The HITL application service providing access to the simulator and GUI drawer.
|
| user_index |
int |
Yes |
The index of the user for whom placement visuals are rendered.
|
| gravity_dir |
mn.Vector3 |
No |
The gravity direction vector. Defaults to (0, -1, 0).
|
Inputs (update)
| Name |
Type |
Required |
Description
|
| ray |
Ray |
Yes |
The ray to cast into the scene for placement (typically from the mouse).
|
| query_obj_id |
int |
Yes |
The ID of the object being placed.
|
Outputs
| Name |
Type |
Description
|
| update() return value |
Optional[mn.Vector3] |
The valid placement position if successful, or None if placement failed.
|
| (visual output) |
Rendered circle |
A white circle at valid placement positions or a small red circle at invalid positions.
|
Constants
| Name |
Value |
Description
|
| COLOR_PLACE_PREVIEW_VALID |
mn.Color3(1, 1, 1) |
White color for valid placement preview circle.
|
| COLOR_PLACE_PREVIEW_INVALID |
mn.Color3(1, 0, 0) |
Red color for invalid placement preview circle.
|
| RADIUS_PLACE_PREVIEW_VALID |
0.25 |
Radius of the valid placement preview circle.
|
| RADIUS_PLACE_PREVIEW_INVALID |
0.05 |
Radius of the invalid placement preview circle.
|
| FAR_AWAY_HIDDEN_POSITION |
mn.Vector3(0, -1000, 0) |
Position used to hide objects during raycast.
|
| DEFAULT_GRAVITY |
mn.Vector3(0, -1, 0) |
Default gravity direction.
|
Usage Examples
Basic Usage
from habitat_hitl.environment.gui_placement_helper import GuiPlacementHelper
# Create a placement helper for user 0
placement_helper = GuiPlacementHelper(
app_service=app_service,
user_index=0,
)
# Each frame, when the user is holding an object:
ray = app_service.gui_input.mouse_ray
if ray is not None and held_object_id is not None:
placement_pos = placement_helper.update(ray, held_object_id)
if placement_pos is not None:
print(f"Valid placement at {placement_pos}")
# On click, finalize placement
if gui_input.get_mouse_button_down(MouseButton.LEFT):
place_object(held_object_id, placement_pos)
Related Pages
Page Connections
Double-click a node to navigate. Hold to expand connections.