Clash Detection UI - Developer#

This page documents the developer-facing API of the omni.physx.clashdetection.ui extension: carb settings paths, custom configuration hooks, the filtering and grouping APIs, person-in-charge management, selection pub/sub, and the viewport bridge used by the Clash Detection UI.

Warning

No stability guarantees. All APIs documented on this page are internal implementation details of the omni.physx.clashdetection.ui sample UI extension. They are not part of the official Clash Detection public API.

  • Classes, functions, signatures, module paths, and carb.settings keys may be renamed, moved, or removed in any release without prior notice and without a deprecation period.

  • No backwards-compatibility commitment is made for any symbol documented here.

  • Do not rely on these APIs in production code or shipping extensions.

The stable, supported API is provided by the lower-level extensions omni.physx.clashdetection (C++ engine) and omni.physx.clashdetection.core (Python data structures and serialization). See Clash Detection Core API for the supported developer interface.

Settings Paths#

All persistent UI settings can be read or written programmatically using carb.settings.get_settings(). The table below maps each UI option to its carb.settings path (constants live in ExtensionSettings inside omni.physxclashdetectionui.settings):

Setting

carb.settings path

Show / hide the Clash Detection window

/physics/showClashDetectionWindow

Debug logging (also enables telemetry and anim/point-cloud logging)

/persistent/clashDetection/debugLogging

Show full prim paths of clashing objects in the results table

/persistent/clashDetection/showFullPaths

Use asynchronous clash detection pipeline

/persistent/clashDetection/useAsyncClashPipeline

Immediate viewport update during timeline slider drag

/persistent/clashDetection/clashTimelineSliderImmediateUpdate

Auto-save query on selection change (Query Management window)

/persistent/clashDetection/queryWndAutoSave

Display and edit timecodes as frames

/persistent/clashDetection/queryWndTimecodesAsFrames

Show empty groups in the Clash Groups window

/persistent/clashDetection/groupsWndShowEmptyGroups

Example - open the window and enable async pipeline programmatically:

import carb.settings
settings = carb.settings.get_settings()
settings.set_bool("/physics/showClashDetectionWindow", True)
settings.set_bool("/persistent/clashDetection/useAsyncClashPipeline", True)

Persons-in-Charge API#

The Person in Charge column in the results table is populated from a JSON file. By default the file at <extension_path>/omni/physxclashdetectionui/pic.json is used.

To load a custom list before the extension starts, set ExtensionSettings.pic_file_path to the path of your JSON file:

from omni.physxclashdetectionui.settings import ExtensionSettings
ExtensionSettings.pic_file_path = "/path/to/my_pic.json"

The JSON file must contain a list of person objects with username, first_name, last_name, and email fields. An entry with an empty username acts as the “unassigned” placeholder.

The following classes from omni.physxclashdetectionui.pic_provider implement the person-in-charge data model:

class PersonInCharge(
username: str = '',
first_name: str = '',
last_name: str = '',
email: str = '',
)#

Represents a single person who can be assigned responsibility for a clash result.

Each person is uniquely identified by their username. An instance with an empty username is used as the “unassigned” placeholder and is always present in the PersonsInCharge collection.

Parameters:
  • username (str) – Unique identifier for the person. Empty string is allowed.

  • first_name (str) – First name.

  • last_name (str) – Last name.

  • email (str) – Email address.

username: str

Read-only. Unique identifier for the person.

first_name: str

Read-only. First name.

last_name: str

Read-only. Last name.

email: str

Read-only. Email address.

full_name: str

Read-only. Derived display name. Returns "first_name last_name" when both fields are set; falls back to whichever is non-empty, or to username if both name fields are empty.

full_name_email: str

Read-only. Returns "<full_name> <email>" when an email address is present, otherwise returns full_name.

__lt__(other) bool#

Enables sorting by full_name.

class PersonsInCharge#

Collection manager for PersonInCharge objects.

Maintains an internal dictionary keyed by username. The unassigned placeholder (username = "") is always present.

pic_none#
Type:

PersonInCharge

Class-level sentinel representing the unassigned person: PersonInCharge("", "<None>"). Returned by get_person() when the requested username is not found.

reset() None#

Clears all entries and re-inserts the pic_none placeholder.

get_items() ValuesView[PersonInCharge]#

Returns a view of all registered PersonInCharge objects (including the unassigned placeholder).

get_person(username: str) PersonInCharge#

Look up a person by username. Never returns None - falls back to pic_none when the username is not found.

Parameters:

username (str) – The username to look up.

Return type:

PersonInCharge

fetch(source: str) bool#

Abstract hook for populating the collection from an external source. The base implementation always returns False. Override in a subclass to load persons from a database, REST endpoint, or other source.

Parameters:

source (str) – Arbitrary source descriptor (path, URL, etc.).

Returns:

True on success, False otherwise.

Return type:

bool

Filtering API#

The filter expressions used in the Custom Advanced Filtering window are parsed and evaluated by two functions and a supporting class in omni.physxclashdetectionui.filtering:

parse_filter_expression(
filter_expression: str,
upper_cased: bool = True,
) FilterNode | None#

Parse a filter expression string into a tree of FilterNode objects.

Parameters:
  • filter_expression (str) – The filter expression string.

  • upper_cased (bool) – If True (default), column names and string literals are upper-cased for case-insensitive matching.

Returns:

Root FilterNode if parsing succeeds; None if the expression is malformed (errors are logged via carb.log_error).

Return type:

FilterNode | None

Raises:

SyntaxError – On malformed expressions (caught internally; None is returned).

apply_filter(
filter_tree: FilterNode | None,
get_column_value_fn: Callable[[str], Any],
) bool#

Evaluate a FilterNode tree against a single data row.

Parameters:
  • filter_tree (FilterNode | None) – Root node of the parsed filter tree, or None for “no filter”.

  • get_column_value_fn (Callable[[str], Any]) – Callable that accepts a column name (str) and returns the value for that column in the current row.

Returns:

True if the row matches the filter (or if filter_tree is None).

Return type:

bool

class FilterNode#

A node in a filter expression parse tree.

Leaf nodes represent comparisons; branch nodes represent AND/OR logic.

column: str

Column name for comparison nodes (None on logic nodes).

op: str

Comparison operator, one of: =, <, >, <=, >=, <>, !=, IN, NOT IN, LIKE, NOT LIKE.

value#

Right-hand operand: a str, int, float, or list (for IN/NOT IN). May also be a "[ColumnName]" string for column-to-column comparisons.

left: FilterNode

Left child (for logic nodes).

right: FilterNode

Right child (for logic nodes).

logic: str

Logical operator "AND" or "OR" (None on comparison leaf nodes).

__repr__() str#

Returns "(<column> <op> <value>)" for leaf nodes, or "(<left> <logic> <right>)" for logic nodes.

Grouping API#

The grouping API in omni.physxclashdetectionui.grouping organizes flat clash result rows into a USD-prim hierarchy by walking each clashing object’s path upward until a prim of a recognized kind is found. The resulting tree is represented as a dictionary of GroupNode objects.

class GroupNode(group_path: Sdf.Path, group_kind: str = '')#

Hierarchical node for grouping clash results by USD prim path and kind.

Each node represents one USD prim and collects the clash row items whose paths fall under it, plus references to its child group nodes.

Parameters:
  • group_path (Sdf.Path) – The USD prim path for this group node.

  • group_kind (str) – The USD kind metadata string for this node (e.g. "component", "group"). Empty string for the root node.

group_path: Sdf.Path

Read-only. The USD prim path for this node.

group_kind: str

Read-only. The USD kind metadata for this node.

children: set[GroupNode]

Read-only. The set of direct child group nodes.

clashing_pairs: set[ClashDetectTableRowItem]

Read-only. Clash result items assigned directly to this node (not to its children).

total_clashes: int

Total number of clashes in this node and all its descendants. Updated by group() after the tree is built; can also be set directly.

add_clashing_pair(row_item: ClashDetectTableRowItem) None#

Add a clash result row item to this node’s clashing_pairs set.

add_child(child: GroupNode) None#

Add a child GroupNode to this node’s children set.

group(
stage: Usd.Stage,
clashing_pairs_list: List[ClashDetectTableRowItem],
root_prim_path: Sdf.Path | None = None,
kinds: List[str] | None = None,
discard_empty_groups: bool = False,
) Dict[Sdf.Path | None, GroupNode]#

Build a tree of GroupNode objects from a flat list of clash row items.

For each row item both object_a_path and object_b_path are walked up the USD hierarchy. The first ancestor prim whose kind matches any entry in kinds becomes the owning group node. If no such ancestor exists the row is assigned to the root group node.

Parameters:
  • stage (Usd.Stage) – The USD stage to operate on.

  • clashing_pairs_list (List[ClashDetectTableRowItem]) – List of clash row items to group.

  • root_prim_path (Optional[Sdf.Path]) – Path to use as the tree root. Defaults to the stage pseudo-root when None.

  • kinds (Optional[List[str]]) – Prim kinds to treat as grouping boundaries. Defaults to ["component", "subcomponent", "group"].

  • discard_empty_groups (bool) – When True, groups with no direct clashing_pairs are pruned from the hierarchy; their non-empty children are promoted one level up.

Returns:

A dictionary mapping each group’s Path to its GroupNode. The root node is accessible via both node_dict[root_prim_path] and node_dict[None].

Return type:

Dict[Optional[Sdf.Path], GroupNode]

dump_groups(
node_dict: Dict[Sdf.Path | None, GroupNode],
node: GroupNode,
indent: int = 0,
) None#

Recursively pretty-print the group hierarchy to stdout.

For each node the output includes the USD path, kind, total clash count, child count, and the individual clashing pairs. Children and pairs are both printed in sorted order.

Parameters:
  • node_dict (Dict[Optional[Sdf.Path], GroupNode]) – Mapping from Path to GroupNode (as returned by group()).

  • node (GroupNode) – Root node to start printing from.

  • indent (int) – Number of leading spaces for the current level. Defaults to 0.

dump_groups_to_json(
node_dict: Dict[Sdf.Path | None, GroupNode],
node: GroupNode,
indent: int = 0,
) None#

Recursively serialize the group hierarchy to JSON and print to stdout.

Each node is represented as a JSON object with keys: group_path (str), group_kind (str), total_clashes (int), children (list), and clashing_pairs (list of {"object_a_path": …, "object_b_path": …} dicts). Children and pairs are sorted by path string.

Parameters:
  • node_dict (Dict[Optional[Sdf.Path], GroupNode]) – Mapping from Path to GroupNode (as returned by group()).

  • node (GroupNode) – Root node to serialize.

  • indent (int) – Unused indentation parameter kept for API consistency. JSON indentation is always 2.

Example - group clashes and print the tree:

from pxr import Sdf, Usd
from omni.physxclashdetectionui.grouping import group, dump_groups, dump_groups_to_json

node_dict = group(
    stage=stage,
    clashing_pairs_list=clash_rows,
    root_prim_path=Sdf.Path("/World"),
    kinds=["component", "subcomponent"],
    discard_empty_groups=True,
)

root_node = node_dict[None]  # or node_dict[Sdf.Path("/World")]
dump_groups(node_dict, root_node)
dump_groups_to_json(node_dict, root_node)

Clash Selection API#

omni.physxclashdetectionui.selection.clash_selection provides a lightweight publish/subscribe mechanism for tracking the active query and the set of currently selected ClashInfo objects.

Type aliases:

  • QueryId = NewType("QueryId", int)

  • SelectionContainer = tuple[ClashInfo, ...]

class ClashSelection#

Tracks the current query ID, timecode, and selected clash infos. Multiple subsystems (viewport bridge, UI panels) subscribe to its change notifications rather than polling.

query_id: QueryId

Read-only. The ID of the currently active query.

timecode: float

Read-only. The timecode associated with the most recent selection update or explicit set_current_timecode() call.

selection: SelectionContainer

Read-only. Immutable tuple of the currently selected ClashInfo objects.

clear_selection() None#

Clears the current selection and broadcasts the change to all selection listeners. Does nothing if the selection is already empty.

set_current_timecode(timecode: float) None#

Sets the current timecode and notifies all timecode listeners.

Parameters:

timecode (float) – New timecode value.

update_query_id(query_id: QueryId) None#

Changes the active query ID, clears the selection, and broadcasts the change to all selection listeners.

Parameters:

query_id (QueryId) – New query ID.

update_selection(
timecode: float,
new_selection: Sequence[ClashInfo],
) None#

Replaces the selection and timecode simultaneously, then broadcasts the change to all selection listeners.

Parameters:
  • timecode (float) – Timecode for the new selection.

  • new_selection (Sequence[ClashInfo]) – New sequence of selected clash infos.

subscribe_to_selection_changes(
callback: Callable,
) ListenerSubscription#

Register a zero-argument callback that is called whenever the selection or query ID changes.

Parameters:

callback – Callable with signature () -> None.

Returns:

A ListenerSubscription handle. The subscription is active as long as the handle is alive; set it to None to unsubscribe.

Return type:

ListenerSubscription

subscribe_to_timecode_changes(
callback: Callable,
) ListenerSubscription#

Register a zero-argument callback that is called whenever the timecode changes.

Parameters:

callback – Callable with signature () -> None.

Returns:

A ListenerSubscription handle.

Return type:

ListenerSubscription

Viewport Bridge API#

omni.physxclashdetectionui.clash_viewport_bridge connects the UI clash selection state to the clash detection viewport extension. It subscribes to ClashSelection changes and forwards them to get_api_instance() so that the viewport renders the correct clash meshes.

class ClashViewportBridge(
clash_data: ClashData,
clash_selection: ClashSelection,
)#

Manages the interaction between clash detection data and the viewport.

On construction the bridge subscribes to both selection changes and timecode changes from clash_selection, and registers a lazy data-loader callback with the viewport API so that per-frame clash data is fetched from the database on demand.

Parameters:
destroy() None#

Releases all subscriptions and clears references. Call this when the bridge is no longer needed to avoid memory leaks.

display_clash_by_clash_info(
clash_infos: Sequence[ClashInfo],
timecode: float,
) None#

Forward a list of ClashInfo objects to the viewport for display at the given timecode. The number of items passed is capped by the CLASH_MESHES_DISPLAY_LIMIT viewport setting.

Parameters:
  • clash_infos (Sequence[ClashInfo]) – Clashes to display.

  • timecode (float) – The USD timecode at which to display the clash meshes.

set_display_clashes_in_main_viewport(
display_clashes_in_main_viewport: bool,
) None#

Convenience wrapper that sets both MAIN_VIEWPORT_SHOW_CLASH_MESHES and MAIN_VIEWPORT_SHOW_CLASH_OUTLINES viewport settings to the given value.

Parameters:

display_clashes_in_main_viewport (bool) – True to show clashes in the main viewport; False to hide them.

hide_all_clash_meshes() None#

Delegates to the viewport API to hide all currently displayed clash meshes. Called automatically on timecode changes.

setup_for_screenshot_export() None#

Configures viewport settings for screenshot export. Saves the current values of MAIN_VIEWPORT_SHOW_CLASH_MESHES, CLASH_VIEWPORT_SHOW_CLASHES, MAIN_VIEWPORT_CENTER_CAMERA, CLASH_VIEWPORT_CENTER_CAMERA, MAIN_VIEWPORT_ENABLE_CAMERA_TOLERANCE, and CLASH_VIEWPORT_ENABLE_CAMERA_TOLERANCE, then applies the export-friendly configuration (clash viewport active, camera centering enabled, etc.).

Raises:

Exception – If the omni.physx.clashdetection.viewport extension is not loaded.

teardown_for_screenshot_export() None#

Restores the viewport settings saved by setup_for_screenshot_export().

Raises:

Exception – If the omni.physx.clashdetection.viewport extension is not loaded.

static export_screenshot_to_file(
clash_info: ClashInfo,
timecode: float,
clash_screenshot_path: str,
current_export_index: int,
total_number_of_exports: int,
clear_frames: int = 5,
render_frames: int = 100,
) None#

Coroutine - must be awaited (await ClashViewportBridge.export_screenshot_to_file(...)).

Export a screenshot of one clash to a file using the Clash Detection Viewport.

The coroutine:

  1. Ensures the Clash Detection Viewport window is visible.

  2. Seeks the timeline to timecode.

  3. Waits clear_frames app updates to clear the previous render.

  4. Calls display_clashes on the viewport API.

  5. Waits for stage loading to finish (up to 15 frames).

  6. Waits render_frames app updates for translucent materials to settle.

  7. Captures the viewport to a temporary PNG and copies it to clash_screenshot_path.

  8. After the last export, calls hide_all_clash_meshes() to clean up.

Parameters:
  • clash_info (ClashInfo) – The clash to display.

  • timecode (float) – Timeline position for the screenshot.

  • clash_screenshot_path (str) – Destination path for the screenshot file.

  • current_export_index (int) – Zero-based index of this export within the batch.

  • total_number_of_exports (int) – Total number of exports in the batch.

  • clear_frames (int) – App updates to wait before displaying the clash. Defaults to 5.

  • render_frames (int) – App updates to wait for rendering to settle. Defaults to 100.

Raises:
  • Exception – If the viewport extension is not loaded or the viewport window could not be created.

  • asyncio.CancelledError – If the export is cancelled; clash meshes are hidden before re-raising.