Example cleanup#580
Conversation
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
…e template. Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
📝 WalkthroughWalkthroughAdds ModelBuilder.replicate; refactors viewer architecture to decouple construction from model binding, adds ViewerNull, wind forces, point rendering, and vsync controls; updates GL instancing and utilities; reworks Rerun/USD viewers to new API; overhauls examples to viewer-driven flow with CUDA graph capture; expands tests; updates docs and assets handling. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant App as Example
participant Viewer as ViewerBase/GL/Rerun/USD
participant Model as Model
participant Solver as SolverXPBD/…
User->>App: create viewer via newton.examples.init()
App->>Viewer: set_model(Model)
Note over Viewer: Binds device, populates shapes
loop per frame
App->>Viewer: begin_frame(time)
App->>Solver: clear_forces()
App->>Viewer: apply_forces(state) # picking/wind guards if no model
App->>Model: collide(state, contacts)
App->>Solver: step(state, control, contacts, dt)
App->>Viewer: log_state(state)
App->>Viewer: log_contacts(state, contacts)
App->>Viewer: end_frame()
end
sequenceDiagram
autonumber
participant Builder as ModelBuilder
participant Sub as Sub-Builder
participant Grid as _compute_replicate_offsets
Builder->>Grid: compute offsets(num_copies, spacing)
Grid-->>Builder: list[vec3] offsets
loop i in num_copies
Builder->>Builder: add_builder(Sub, xform=transform(offsets[i], quat_identity))
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120–180 minutes Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
newton/_src/viewer/__init__.py (1)
22-38: Example still shows ViewerGL(model) — update to new model binding flow.Viewers no longer require a model at construction. The snippet should reflect set_model.
Apply this doc update:
- # Create viewer with OpenGL backend - viewer = ViewerGL(model) + # Create viewer with OpenGL backend + viewer = ViewerGL() + viewer.set_model(model)newton/_src/viewer/viewer_rerun.py (2)
61-68: Potential UnboundLocalError: server_uri used when not defined; address argument unusedIf server=False, server_uri is never set but is referenced when launching the viewer. Also, the address parameter is not used, preventing client-mode connections.
- # Set up connection based on mode - if self.server: - server_uri = rr.serve_grpc() - - # Optionally launch viewer client - if self.launch_viewer: - rr.serve_web_viewer(connect_to=server_uri) + # Set up connection based on mode + server_uri = None + if self.server: + # Start an in-process server; let rerun pick an available port + server_uri = rr.serve_grpc() + else: + # Connect to an existing server + try: + rr.connect(self.address) + except Exception as e: + raise RuntimeError(f"Failed to connect to rerun server at {self.address}: {e}") from e + + # Optionally launch viewer client + if self.launch_viewer: + rr.serve_web_viewer(connect_to=server_uri)
151-157: vertex_colors may be referenced before assignmentWhen colors is None, vertex_colors is undefined but still passed to rr.Mesh3D, causing UnboundLocalError. Default to None.
- # Log the base mesh with optional colors + # Log the base mesh with optional colors + vertex_colors = vertex_colors if 'vertex_colors' in locals() else None mesh_3d = rr.Mesh3D( vertex_positions=mesh_data["points"], triangle_indices=mesh_data["indices"], vertex_normals=mesh_data["normals"], vertex_colors=vertex_colors, )Alternatively, set vertex_colors = None before the if block.
🧹 Nitpick comments (17)
newton/_src/viewer/viewer_usd.py (2)
20-26: Docstring default contradicts code (fps default 24 vs 60).The Args section says fps default is 24, but the constructor uses 60. Align the docs or the code.
Apply this diff to fix the docstring:
- fps: Frames per second for time sampling (default: 24) + fps: Frames per second for time sampling (default: 60)
207-214: PointInstancer ids/protoIndices authored only once — can desync when instance count changes across frames.If num_instances varies over time, fixed ids/protoIndices authored “once” will mismatch attribute lengths and cause viewer issues. Re-author when counts differ.
Apply this diff:
- # Initialize ids and protoIndices once for count stability - ids_attr = instancer.GetIdsAttr() - if ids_attr.GetNumTimeSamples() == 0 and not ids_attr.HasAuthoredValueOpinion(): - instancer.CreateIdsAttr().Set(list(range(num_instances))) - instancer.CreateProtoIndicesAttr().Set([0] * num_instances) + # Ensure ids/protoIndices length matches current instance count + ids_attr = instancer.GetIdsAttr() + existing_ids = ids_attr.Get() if ids_attr.IsValid() else None + if existing_ids is None or len(existing_ids) != num_instances: + instancer.CreateIdsAttr().Set(list(range(num_instances))) + instancer.CreateProtoIndicesAttr().Set([0] * num_instances)newton/_src/sim/builder.py (1)
575-582: Nit: Ensure transform arg types are explicit vec3 to avoid Warp coercion surprises.Passing a NumPy row into wp.transform usually works, but being explicit improves readability and type clarity.
Apply this diff:
- for i in range(num_copies): - self.add_builder(builder, xform=wp.transform(offsets[i], wp.quat_identity())) + for i in range(num_copies): + p = wp.vec3(float(offsets[i][0]), float(offsets[i][1]), float(offsets[i][2])) + self.add_builder(builder, xform=wp.transform(p, wp.quat_identity()))newton/examples/__init__.py (1)
50-56: Deprecation warning: good placement and stacklevelThe explicit warning and stacklevel are appropriate. Consider adding a brief pointer to docs/migration.rst in the message to aid users migrating from compute_env_offsets to builder.replicate().
- warnings.warn( - "compute_env_offsets is deprecated and will be removed in a future version. Use the builder.replicate() function instead.", + warnings.warn( + "compute_env_offsets is deprecated and will be removed in a future version. Use builder.replicate() instead. See docs/migration.rst for details.", stacklevel=2, )newton/examples/basic/example_basic_pendulum.py (3)
16-26: Header comment is incorrect for this exampleThe header says “Example Basic URDF” and references a quadruped/URDF, but this file implements a pendulum example. Please correct to avoid confusion.
-########################################################################### -# Example Basic URDF +########################################################################### +# Example Basic Pendulum @@ -# Shows how to set up a simulation of a rigid-body quadruped articulation -# from a URDF using the newton.ModelBuilder(). +# Shows how to set up and simulate a simple two-link pendulum using +# newton.ModelBuilder(). @@ -# Users can pick bodies by right-clicking and dragging with the mouse. +# Users can pick bodies by right-clicking and dragging with the mouse (in ViewerGL).
59-64: Consider specifying body mass to ensure dynamic behaviorBy default, ModelBuilder.add_body() may create kinematic bodies if mass is 0. If that’s the case in this codebase, the pendulum won’t move. Consider setting a positive mass per link or using builder defaults to ensure dynamic simulation.
Would you like a patch that sets per-link masses and inertia approximations based on the box dimensions?
99-101: Minor grammar: “its” vs “it’s”“It’s” is a contraction; possessive is “its.”
- # put graph capture into it's own function + # put graph capture into its own functionnewton/_src/viewer/viewer_rerun.py (2)
204-210: is_running may never terminate in run(viewer, example) flowsWithout a spawned viewer process or an external stop signal, is_running returns self._running which is never toggled, causing infinite loops under newton.examples.run. Consider adding an optional num_frames limit (parity with ViewerNull) or respecting rr.is_recording/connection status.
Would you like a patch to add a num_frames parameter and increment an internal frame counter in begin_frame()/end_frame()?
216-225: _viewer_process lifecycle handling appears unusedYou never assign to self._viewer_process; terminate/kill paths won’t execute. Either store the process handle from rr.serve_web_viewer (if available) or remove the dead code.
- self._viewer_process = None + self._viewer_process = None # Consider capturing a handle if rr exposes it; otherwise remove related code.If rr.serve_web_viewer doesn’t return a Popen-like handle, remove the terminate/kill branches to avoid confusion.
newton/_src/viewer/viewer_null.py (1)
46-51: Update begin_frame to retain ViewerBase timeFor consistency with other backends, call super().begin_frame(time) so self.time is tracked (useful for logs or future extensions).
- def begin_frame(self, time): - pass + def begin_frame(self, time): + super().begin_frame(time)newton/examples/basic/example_basic_viewer.py (4)
82-85: Fix viewer type check (always false currently).
vieweris now an object, not a string. The condition on Line 82 never triggers, so the usage hint is never printed for the GL backend.Apply this diff to correctly detect the GL backend:
- if viewer == "gl": - print("Viewer running. WASD/Arrow keys to move, drag to orbit, scroll to zoom. Close window to exit.") + # Only print usage hints for interactive GL viewer + try: + from newton.viewer import ViewerGL as _ViewerGL + if isinstance(viewer, _ViewerGL): + print("Viewer running. WASD/Arrow keys to move, drag to orbit, scroll to zoom. Close window to exit.") + except Exception: + pass
170-177: Align plane geo_scale with ViewerBase.log_geo contract.
log_shapes(..., GeoType.PLANE, (0.0, 0.0, 1.0, 0.0), ...)passes 4 values that are ignored by the plane path (only width/length are used). This is confusing and suggests plane-equation parameters, which aren’t supported here.Use a 2-tuple (or a single float) to convey “infinite” plane intent:
- self.viewer.log_shapes( - "/plane_instance", - newton.GeoType.PLANE, - (0.0, 0.0, 1.0, 0.0), - wp.array([wp.transform_identity()], dtype=wp.transform), - self.col_plane, - self.mat_plane, - ) + self.viewer.log_shapes( + "/plane_instance", + newton.GeoType.PLANE, + (0.0, 0.0), # non-positive -> treated as a large ground plane + wp.array([wp.transform_identity()], dtype=wp.transform), + self.col_plane, + self.mat_plane, + )
94-106: Comment/axis mismatch (Y used, comment says X).The layout uses the Y coordinate (Line 106 uses base_left as Y), but comments say “arrange along X-axis” and “Sphere at x = -6”. Consider aligning comments with the actual axis used to avoid confusion.
Apply this diff to fix comments:
- # Clean layout: arrange objects in a line along X-axis + # Clean layout: arrange objects in a line along Y-axis ... - # Sphere: gentle bounce at x = -6 + # Sphere: gentle bounce at y ≈ -4
179-179: Clarify log_lines path name.
"coordinate_self.axes"looks like a typo and is inconsistent with other log paths that use a leading slash.Rename for consistency:
- self.viewer.log_lines("coordinate_self.axes", self.axes_begins, self.axes_ends, self.axes_colors) + self.viewer.log_lines("/axes", self.axes_begins, self.axes_ends, self.axes_colors)newton/_src/viewer/viewer.py (1)
160-163: Fix comment color mismatch.Comment says orange-red, but the color is green.
Minimal correction:
- # Use orange-red color for contact normals + # Use green color for contact normals line_colors = (0.0, 1.0, 0.0)newton/tests/test_examples_new.py (2)
100-107: Ensure viewer cleanup even on exceptions.Wrap the frame loop in try/finally to guarantee
viewer.close()runs. Reduces resource leaks and stray open windows/files on failure.Apply this diff:
- # run for N frames - for _ in range(num_frames): - example.step() - example.render() - example.test() - - viewer.close() + try: + # run for N frames + for _ in range(num_frames): + example.step() + example.render() + example.test() + finally: + try: + viewer.close() + except Exception: + pass
85-88: Remove or use unused env_vars.
env_varsis computed but never used. Either setos.environ["WARP_CACHE_PATH"]inline or drop the block.One-liner fix:
- env_vars = os.environ.copy() - if warp_cache_path is not None: - env_vars["WARP_CACHE_PATH"] = warp_cache_path + if warp_cache_path is not None: + os.environ["WARP_CACHE_PATH"] = warp_cache_path
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
newton/_src/sim/builder.py(1 hunks)newton/_src/viewer/__init__.py(1 hunks)newton/_src/viewer/gl/shaders.py(0 hunks)newton/_src/viewer/picking.py(2 hunks)newton/_src/viewer/viewer.py(9 hunks)newton/_src/viewer/viewer_gl.py(8 hunks)newton/_src/viewer/viewer_null.py(1 hunks)newton/_src/viewer/viewer_rerun.py(1 hunks)newton/_src/viewer/viewer_usd.py(2 hunks)newton/examples/__init__.py(3 hunks)newton/examples/basic/example_basic_pendulum.py(1 hunks)newton/examples/basic/example_basic_urdf.py(1 hunks)newton/examples/basic/example_basic_viewer.py(6 hunks)newton/tests/test_examples_new.py(1 hunks)newton/viewer.py(1 hunks)
💤 Files with no reviewable changes (1)
- newton/_src/viewer/gl/shaders.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
🧬 Code Graph Analysis (10)
newton/_src/viewer/__init__.py (1)
newton/_src/viewer/viewer_null.py (1)
ViewerNull(22-69)
newton/viewer.py (1)
newton/_src/viewer/viewer_null.py (1)
ViewerNull(22-69)
newton/examples/basic/example_basic_urdf.py (4)
newton/_src/sim/builder.py (3)
ModelBuilder(68-4112)replicate(575-581)add_ground_plane(2201-2221)newton/examples/__init__.py (2)
get_asset(32-33)run(36-44)newton/_src/viewer/viewer.py (6)
set_model(66-74)apply_forces(367-368)begin_frame(76-77)log_state(79-100)log_contacts(102-163)end_frame(371-372)newton/_src/viewer/viewer_gl.py (6)
set_model(94-100)apply_forces(262-264)begin_frame(243-244)log_state(201-208)end_frame(246-260)ViewerGL(33-1054)
newton/examples/__init__.py (6)
newton/tests/test_examples_new.py (1)
run(65-113)newton/_src/viewer/viewer_usd.py (2)
is_running(258-262)close(264-267)newton/_src/viewer/viewer_null.py (2)
is_running(52-53)close(55-56)newton/_src/viewer/viewer_rerun.py (2)
is_running(204-209)close(211-230)newton/_src/viewer/viewer_gl.py (2)
is_running(297-299)close(305-307)newton/_src/viewer/viewer.py (1)
close(375-376)
newton/examples/basic/example_basic_pendulum.py (3)
newton/_src/sim/builder.py (5)
ModelBuilder(68-4112)add_articulation(583-589)add_shape_box(2256-2293)add_joint_revolute(1062-1144)add_ground_plane(2201-2221)newton/_src/viewer/viewer.py (7)
finalize(401-409)set_model(66-74)apply_forces(367-368)begin_frame(76-77)log_state(79-100)log_contacts(102-163)end_frame(371-372)newton/examples/__init__.py (1)
run(36-44)
newton/_src/viewer/viewer_null.py (3)
newton/_src/viewer/viewer.py (10)
ViewerBase(33-649)log_mesh(334-344)log_instances(347-348)begin_frame(76-77)end_frame(371-372)close(375-376)log_lines(351-352)log_points(355-356)log_array(359-360)log_scalar(363-364)newton/_src/viewer/viewer_usd.py (10)
log_mesh(60-115)log_instances(117-241)begin_frame(243-252)end_frame(254-256)is_running(258-262)close(264-267)log_lines(270-272)log_points(274-276)log_array(278-280)log_scalar(282-284)newton/_src/viewer/viewer_gl.py (10)
log_mesh(102-122)log_instances(124-136)begin_frame(243-244)end_frame(246-260)is_running(297-299)close(305-307)log_lines(138-190)log_points(192-193)log_array(195-196)log_scalar(198-199)
newton/tests/test_examples_new.py (3)
newton/tests/unittest_utils.py (4)
add_function_test(281-300)get_selected_cuda_test_devices(56-95)get_test_devices(98-132)sanitize_identifier(271-278)newton/_src/viewer/viewer_usd.py (2)
ViewerUSD(14-317)close(264-267)newton/_src/viewer/viewer_null.py (1)
ViewerNull(22-69)
newton/_src/viewer/viewer_gl.py (4)
newton/_src/viewer/gl/opengl.py (1)
RendererGL(689-1488)newton/_src/viewer/viewer.py (2)
set_model(66-74)begin_frame(76-77)newton/_src/viewer/picking.py (2)
Picking(25-163)pick(101-163)newton/_src/viewer/camera.py (1)
Camera(19-186)
newton/examples/basic/example_basic_viewer.py (8)
newton/examples/basic/example_basic_pendulum.py (4)
Example(36-138)step(123-129)render(134-138)test(131-132)newton/_src/viewer/viewer_usd.py (4)
begin_frame(243-252)log_lines(270-272)end_frame(254-256)ViewerUSD(14-317)newton/_src/viewer/viewer.py (4)
begin_frame(76-77)log_shapes(165-246)log_lines(351-352)end_frame(371-372)newton/_src/viewer/viewer_null.py (3)
begin_frame(46-47)log_lines(59-60)end_frame(49-50)newton/_src/viewer/viewer_rerun.py (4)
begin_frame(193-197)log_lines(233-234)end_frame(199-202)ViewerRerun(30-243)newton/_src/viewer/viewer_gl.py (4)
begin_frame(243-244)log_lines(138-190)end_frame(246-260)ViewerGL(33-1054)newton/_src/geometry/types.py (1)
GeoType(25-36)newton/examples/__init__.py (1)
run(36-44)
newton/_src/viewer/viewer.py (1)
newton/_src/viewer/viewer_gl.py (3)
set_model(94-100)log_lines(138-190)log_mesh(102-122)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (24)
newton/_src/viewer/viewer_usd.py (3)
217-241: Per-instance colors on PointInstancer likely need 'uniform' interpolation, not 'vertex'.For PointInstancer primvars, per-instance data is typically authored with uniform interpolation. Using vertex here may be ignored by viewers.
Would you confirm with USD docs/viewer behavior? If needed, change interpolation:
- col_pv = pv_api.CreatePrimvar( - "displayColor", - Sdf.ValueTypeNames.Color3fArray, - UsdGeom.Tokens.vertex, - ) + col_pv = pv_api.CreatePrimvar( + "displayColor", + Sdf.ValueTypeNames.Color3fArray, + UsdGeom.Tokens.uniform, + )
264-268: LGTM: Explicitly releasing the stage reference on close.Saving the root layer and nulling self.stage is a sensible cleanup.
28-33: All ViewerUSD instantiations updated to new constructor signatureNo positional
modelargument was found in tests or examples—only keyword args (output_path,num_frames, etc.) are used.• newton/tests/test_examples_new.py (Line 91)
• newton/examples/basic/example_basic_viewer.py (Line 198)newton/_src/viewer/__init__.py (1)
42-49: LGTM: ViewerNull added to public exports.Import and all exposure look correct and consistent with the new backend.
newton/viewer.py (1)
21-32: LGTM: Public API now exports ViewerNull.This lines up with the subpackage exports and enables a no-op backend for headless or dependency-light environments.
newton/_src/viewer/picking.py (2)
63-65: Good guard: avoid kernel launch when no model is bound.Prevents invalid device access during early viewer lifecycle.
102-104: Good guard in pick(): skip work when model is None.Consistent with decoupled model binding and avoids unnecessary raycast setup.
newton/examples/__init__.py (2)
36-45: Run loop looks good and aligns with viewer lifecycleThe step/render phasing with ScopedTimer and graceful viewer shutdown is clear and matches backend expectations.
97-101: Public example aliases/export are clearThe aliases and all exports match the new example structure and test harness references.
newton/examples/basic/example_basic_pendulum.py (1)
134-139: Viewer lifecycle usage is correctbegin_frame/log_state/log_contacts/end_frame sequence is consistent with ViewerBase API and backends.
newton/examples/basic/example_basic_urdf.py (3)
75-82: Environment replication and scene composition look goodUsing scene.replicate(quadruped, self.num_envs) followed by a single ground plane is clean and aligns with the deprecation of compute_env_offsets.
91-93: FK precomputation call is appropriateeval_fk prior to stepping helps non-MuJoCo solvers; matches the codebase guidance.
97-104: Scoped CUDA graph capture usage is correctThe capture pattern mirrors the pendulum example and will accelerate steady-state stepping on CUDA devices.
newton/_src/viewer/viewer_rerun.py (1)
73-121: Type conversions and logging to rerun look consistentConverting Warp arrays to numpy and reshaping triangle indices is correct for rr.Mesh3D.
newton/_src/viewer/viewer_null.py (2)
52-56: Frame-limited loop is appropriate for tests/headless runsIncrementing frame_count in end_frame and using it for is_running matches the examples runner expectations and test harness behavior.
31-41: No-op logging methods are fine for a null backendSignatures align with ViewerBase; keeping them as no-ops is appropriate.
newton/examples/basic/example_basic_viewer.py (1)
36-51: LGTM: Nice decoupling and per-instance style setup.Constructor changes and per-instance color/material arrays are clean and align with the new viewer-driven flow.
newton/_src/viewer/viewer.py (3)
66-75: LGTM: Deferred model binding via set_model.Single-assignment semantics and calling _populate_shapes() on bind are clear and make device ownership explicit.
111-115: LGTM: Contact logging properly guards the disabled case.Early clearing with
Nonebuffers prevents stale visuals.
539-546: LGTM: Instances batching uses viewer.device consistently.Batch device propagation looks correct and matches the set_model device selection.
newton/_src/viewer/viewer_gl.py (3)
243-246: LGTM: begin_frame now calls base for consistent timekeeping.This aligns with ViewerBase semantics and keeps backends consistent.
381-387: LGTM: Right-click picking guarded on state availability.Prevents pick attempts when no state has been logged yet.
691-702: LGTM: Stats overlay guards model access properly.Avoids attribute access when no model is bound.
newton/tests/test_examples_new.py (1)
118-150: LGTM: Clean device-parameterized example registration.Good coverage and device-aware argument injection for CPU/CUDA.
Signed-off-by: Gabriel Noya <mega.kaloca@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/examples/basic/example_basic_viewer.py (1)
101-126: Also create per-frame transform arrays on the viewer’s deviceSame device-mismatch concern for xforms generated every frame. These should be created on the viewer’s device.
Apply this diff:
- x_sphere_anim = wp.array([wp.transform(sphere_pos, qy_slow)], dtype=wp.transform) + x_sphere_anim = wp.array([wp.transform(sphere_pos, qy_slow)], dtype=wp.transform, device=self.device) @@ - x_box_anim = wp.array([wp.transform([0.0, base_left, base_height], qx_slow)], dtype=wp.transform) + x_box_anim = wp.array([wp.transform([0.0, base_left, base_height], qx_slow)], dtype=wp.transform, device=self.device) @@ - x_cone_anim = wp.array([wp.transform([0.0, base_left, base_height], qy_slow)], dtype=wp.transform) + x_cone_anim = wp.array([wp.transform([0.0, base_left, base_height], qy_slow)], dtype=wp.transform, device=self.device) @@ - x_cyl_anim = wp.array([wp.transform([0.0, base_left, base_height], qz_slow)], dtype=wp.transform) + x_cyl_anim = wp.array([wp.transform([0.0, base_left, base_height], qz_slow)], dtype=wp.transform, device=self.device) @@ - x_cap_anim = wp.array([wp.transform(capsule_pos, qy_slow)], dtype=wp.transform) + x_cap_anim = wp.array([wp.transform(capsule_pos, qy_slow)], dtype=wp.transform, device=self.device) @@ - wp.array([wp.transform_identity()], dtype=wp.transform), + wp.array([wp.transform_identity()], dtype=wp.transform, device=self.device),Also applies to: 170-175
🧹 Nitpick comments (12)
newton/examples/basic/example_basic_urdf.py (7)
71-77: Replication is fine; consider explicit spacing or per-env filtering if environments could interactUsing replicate(quadruped, self.num_envs) with default spacing likely avoids cross-env interactions. If you plan to reduce spacing or increase num_envs significantly, consider making spacing explicit or adding per-environment collision filtering to prevent inter-env collisions.
Example (optional):
- scene.replicate(quadruped, self.num_envs) + scene.replicate(quadruped, self.num_envs, spacing=(5.0, 5.0, 0.0)) # explicit grid spacing
93-95: Fix minor grammar: "it's" → "its"Nitpick on comment wording.
- # put graph capture into it's own function + # put graph capture into its own function
96-103: Capture device should match model.device to avoid mismatches on multi-device setupsUsing wp.get_device() reads the default device, which may differ from model.device. Tie capture to the model’s device for correctness.
- if wp.get_device().is_cuda: + if wp.get_device(self.model.device).is_cuda: with wp.ScopedCapture() as capture: self.simulate() self.graph = capture.graph else: self.graph = None
104-116: Contacts logged for rendering can lag one substep; refresh after the loopYou compute contacts before stepping, then swap states; render() logs contacts with the post-step state, which can be visually out of sync by one substep. Refresh contacts after the loop for accurate visualization.
def simulate(self): for _ in range(self.sim_substeps): self.state_0.clear_forces() # apply forces to the model self.viewer.apply_forces(self.state_0) self.contacts = self.model.collide(self.state_0) self.solver.step(self.state_0, self.state_1, self.control, self.contacts, self.sim_dt) # swap states self.state_0, self.state_1 = self.state_1, self.state_0 + # refresh contacts for the post-step state used for rendering + self.contacts = self.model.collide(self.state_0)
125-127: Provide a minimal smoke test implementation or remove the stubEmpty test() can hinder automated example tests. Implement a short smoke step or remove the method if not used by the harness.
- def test(self): - pass + def test(self): + # Smoke test: run a couple of substeps to validate stepping without rendering + for _ in range(2): + self.simulate()If the test harness doesn’t call test(), consider removing the method. I can wire this to newton/tests/test_examples*.py if you’d like.
35-37: Makenum_envsoptional in example_basic_urdf.py for consistencyUpdate the URDF example’s constructor so it matches the other basic examples (which take only
viewer) and avoids breaking calls that omitnum_envs.• File:
newton/examples/basic/example_basic_urdf.py
Change the__init__signature as follows:class Example: - def __init__(self, viewer, num_envs): + def __init__(self, viewer, num_envs=1): # setup simulation parameters first self.fps = 100This lets you call
Example(viewer)just like inexample_basic_pendulum.pyandexample_basic_viewer.py, while still allowing explicit overrides (e.g.num_envs=100).
136-139: Add headless fallback to ViewerNull in example_basic_urdf.pyCreating
ViewerGLcan fail on servers without an OpenGL context (e.g., CI or headless environments). Wrap its construction in atry/exceptand fall back toViewerNull, which is already exported innewton.viewer.• File:
newton/examples/basic/example_basic_urdf.py(around lines 136–139)
• Change:-if __name__ == "__main__": - viewer = newton.viewer.ViewerGL() - example = Example(viewer, num_envs=100) - - newton.examples.run(example) +if __name__ == "__main__": + try: + viewer = newton.viewer.ViewerGL() + except Exception: + # Headless fallback for CI / non-GL environments + viewer = newton.viewer.ViewerNull() + example = Example(viewer, num_envs=100) + newton.examples.run(example)newton/examples/basic/example_basic_viewer.py (5)
94-99: Fix axis mentions in comments (objects are laid out along Y, not X)Comments say X-axis and “x = …”, but base_left increments along Y in transforms.
Apply this diff:
- # Clean layout: arrange objects in a line along X-axis + # Clean layout: arrange objects in a line along Y-axis @@ - # Sphere: gentle bounce at x = -6 + # Sphere: gentle bounce at y = -4 (initial) @@ - # Box: rocking rotation at x = -3 + # Box: rocking rotation at y = -2 @@ - # Cone: spinning at origin (x = 0) + # Cone: spinning at origin (y = 0) @@ - # Cylinder: spinning on different axis at x = 3 + # Cylinder: spinning on different axis at y = +2 @@ - # Capsule: gentle sway at x = 6 + # Capsule: gentle sway at y = +4Also applies to: 105-126
179-179: Tweak log_lines path for readabilityName “coordinate_self.axes” reads odd. Consider a clearer path-like name.
- self.viewer.log_lines("coordinate_self.axes", self.axes_begins, self.axes_ends, self.axes_colors) + self.viewer.log_lines("/debug/axes", self.axes_begins, self.axes_ends, self.axes_colors)
186-187: Optional: add a tiny smoke test to exercise rendering without a windowA short headless smoke in test() helps CI run examples (e.g., with ViewerUSD or ViewerNull). Not required, but useful.
Example implementation (adjust if your test harness provides a viewer):
def test(self): # Render a handful of frames deterministically for _ in range(5): self.render()If you prefer a Null backend, I can open a follow-up PR to add a CLI --viewer null option and wire this example into the example tests.
192-193: Optional: Add “null” viewer backend for headless CIViewerNull is already implemented and exported in
newton.viewer. Adding a"null"choice to the example’s--viewerflag enables faster, window-free CI runs.In
newton/examples/basic/example_basic_viewer.py:• Update the argument parser to include
"null"in the choices.
• Add anelif args.viewer == "null":branch to import and instantiateViewerNull.Proposed diff:
- parser.add_argument("--viewer", choices=["gl", "usd", "rerun"], default="gl", help="Viewer backend to use.") + parser.add_argument("--viewer", choices=["gl", "usd", "rerun", "null"], default="gl", help="Viewer backend to use.")elif args.viewer == "rerun": from newton.viewer import ViewerRerun viewer = ViewerRerun(server=True, launch_viewer=True) + elif args.viewer == "null": + from newton.viewer import ViewerNull + viewer = ViewerNull(num_frames=120)
170-177: Plane geo_scale should be width/length or float, not a 4-tupleThe
log_shapesdoc and implementation expect forGeoType.PLANEa 2-tuple(width, length)or a single float (both dimensions), with non-positive values signalling an “infinite” plane. Passing a 4-tuple(nx, ny, nz, d)(a plane equation) is unsupported—only the first two entries are used (and extra values will pollute the mesh cache).• File:
newton/examples/basic/example_basic_viewer.py
Lines 172–174: replace the 4-tuple with either a 2-tuple or a float.Example diffs:
Option A – infinite plane (default checkerboard):
- newton.GeoType.PLANE, - (0.0, 0.0, 1.0, 0.0), + newton.GeoType.PLANE, + (0.0, 0.0), # (width, length); zeros → infinite planeOption B – finite plane size:
- newton.GeoType.PLANE, - (0.0, 0.0, 1.0, 0.0), + newton.GeoType.PLANE, + 12.0, # width=length=12 units
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
newton/examples/__init__.py(3 hunks)newton/examples/basic/example_basic_pendulum.py(1 hunks)newton/examples/basic/example_basic_urdf.py(1 hunks)newton/examples/basic/example_basic_viewer.py(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- newton/examples/init.py
- newton/examples/basic/example_basic_pendulum.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
🧬 Code Graph Analysis (2)
newton/examples/basic/example_basic_urdf.py (5)
newton/examples/basic/example_basic_pendulum.py (5)
Example(35-133)capture(97-103)simulate(105-116)step(118-124)render(129-133)newton/_src/sim/builder.py (4)
ModelBuilder(68-4112)replicate(575-581)add_ground_plane(2201-2221)finalize(3742-4080)newton/examples/__init__.py (2)
get_asset(32-33)run(36-44)newton/_src/viewer/viewer.py (7)
finalize(401-409)set_model(66-74)apply_forces(367-368)begin_frame(76-77)log_state(79-100)log_contacts(102-163)end_frame(371-372)newton/_src/viewer/viewer_gl.py (6)
set_model(94-100)apply_forces(262-264)begin_frame(243-244)log_state(201-208)end_frame(246-260)ViewerGL(33-1054)
newton/examples/basic/example_basic_viewer.py (6)
newton/_src/viewer/viewer_usd.py (4)
begin_frame(243-252)log_lines(270-272)end_frame(254-256)ViewerUSD(14-317)newton/_src/viewer/viewer.py (4)
begin_frame(76-77)log_shapes(165-246)log_lines(351-352)end_frame(371-372)newton/_src/viewer/viewer_null.py (3)
begin_frame(46-47)log_lines(59-60)end_frame(49-50)newton/_src/viewer/viewer_gl.py (3)
begin_frame(243-244)log_lines(138-190)end_frame(246-260)newton/_src/geometry/types.py (1)
GeoType(25-36)newton/examples/__init__.py (1)
run(36-44)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (6)
newton/examples/basic/example_basic_urdf.py (4)
117-124: LGTM: Graph-based stepping fallback is correctStep() cleanly falls back to simulate() when CUDA graph is unavailable and advances sim_time consistently.
128-133: LGTM: Rendering pipeline aligns with viewer expectationsbegin_frame → log_state → log_contacts → end_frame is consistent with ViewerBase/ViewerGL APIs.
16-26: Migration guide note: This example adds Newton-only functionality; migration doc likely doesn’t need updatesPer the team learning, docs/migration.rst is for warp.sim → Newton migration. This example demonstrates Newton features (URDF + replication) rather than deprecating/replacing a warp.sim API, so migration docs may be unaffected.
If you agree, check off the template item with a note “N/A – Newton-only example (no warp.sim equivalent)”.
61-70: Quadruped URDF asset and joint slicing validated
quadruped.urdfexists atnewton/examples/assets/quadruped.urdf.parse_urdfis defined innewton/_src/utils/import_urdf.py(and exercised in tests).- The trailing
[-12:]slice is used identically inexample_quadruped.pyandexample_mujoco.py, confirming those DOFs map to the four legs.No changes required.
newton/examples/basic/example_basic_viewer.py (2)
92-92: LGTM: deterministic time stepping and begin_frame time plumbingUsing an internal time accumulator and passing it to begin_frame is consistent with USD frame mapping and deterministic playback.
Also applies to: 184-185
195-209: LGTM: backend selection + unified run loopViewer is constructed externally and injected into Example; delegating to newton.examples.run(example) matches the updated architecture.
…e test_examples.py to use new structure Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
…into example-refactor Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
newton/tests/test_examples.py (1)
48-62: Boolean CLI options builder produces duplicate/conflicting flagsThe conditional chain uses two ifs with a single else, causing booleans to emit both “--key/--no-key” and “--key value” forms. This can break argparse. Switch the second if to elif and avoid appending a value for booleans.
Apply this diff:
- for key, value in test_options.items(): - if isinstance(value, bool): - # Default behavior expecting argparse.BooleanOptionalAction support - additional_options.append(f"--{'no-' if not value else ''}{key.replace('_', '-')}") - if isinstance(value, list): - additional_options.extend([f"--{key.replace('_', '-')}"] + [str(v) for v in value]) - else: - # Just add --key value - additional_options.extend(["--" + key.replace("_", "-"), str(value)]) + for key, value in test_options.items(): + arg = f"--{key.replace('_', '-')}" + if isinstance(value, bool): + # Expect argparse.BooleanOptionalAction + additional_options.append(f"--{'no-' if not value else ''}{key.replace('_', '-')}") + elif isinstance(value, list): + additional_options.extend([arg] + [str(v) for v in value]) + else: + additional_options.extend([arg, str(value)])newton/examples/basic/example_basic_viewer.py (1)
104-124: Fix axis placement: base_left is used as Y instead of X; correct transforms and capsule swayComments and layout intent say “arrange objects along X”; current code varies Y. Also make capsule sway around its X position.
Apply this diff:
- # Sphere: gentle bounce at x = -6 - sphere_pos = wp.vec3(0.0, base_left, base_height + 0.3 * abs(math.sin(1.2 * self.time))) + # Sphere: gentle bounce along X + sphere_pos = wp.vec3(base_left, 0.0, base_height + 0.3 * abs(math.sin(1.2 * self.time))) x_sphere_anim = wp.array([wp.transform(sphere_pos, qy_slow)], dtype=wp.transform) base_left += spacing # Box: rocking rotation at x = -3 - x_box_anim = wp.array([wp.transform([0.0, base_left, base_height], qx_slow)], dtype=wp.transform) + x_box_anim = wp.array([wp.transform(wp.vec3(base_left, 0.0, base_height), qx_slow)], dtype=wp.transform) base_left += spacing # Cone: spinning at origin (x = 0) - x_cone_anim = wp.array([wp.transform([0.0, base_left, base_height], qy_slow)], dtype=wp.transform) + x_cone_anim = wp.array([wp.transform(wp.vec3(base_left, 0.0, base_height), qy_slow)], dtype=wp.transform) base_left += spacing # Cylinder: spinning on different axis at x = 3 - x_cyl_anim = wp.array([wp.transform([0.0, base_left, base_height], qz_slow)], dtype=wp.transform) + x_cyl_anim = wp.array([wp.transform(wp.vec3(base_left, 0.0, base_height), qz_slow)], dtype=wp.transform) base_left += spacing # Capsule: gentle sway at x = 6 - capsule_pos = wp.vec3(0.3 * math.sin(0.8 * self.time), base_left, base_height) + capsule_pos = wp.vec3(base_left + 0.3 * math.sin(0.8 * self.time), 0.0, base_height) x_cap_anim = wp.array([wp.transform(capsule_pos, qy_slow)], dtype=wp.transform)Note: You may also want to update the nearby comments that hardcode X positions (e.g., “x = -6”, “x = -3”) since the starting offset is -4.0. Consider removing absolute values or adjusting base_left/spacing to match.
♻️ Duplicate comments (4)
newton/examples/basic/example_basic_pendulum.py (1)
56-70: Correct Warp API usage for transforms — past issue addressedUsing wp.transform with wp.quat_identity() is the right API (vs. non-existent wp.transformf). Hinge frames for both joints look consistent.
newton/_src/viewer/viewer_gl.py (1)
92-101: Set a default Warp device when no model is providedIf set_model(None) is used (as here), self.device stays None and log_mesh/log_lines will fail when allocating arrays. Initialize a sensible default device for model-less flows.
Apply this diff:
def set_model(self, model): - super().set_model(model) + super().set_model(model) + + # Ensure a valid device for model-less workflows (e.g., viewer-only) + if model is None and self.device is None: + self.device = wp.get_device()newton/examples/basic/example_basic_viewer.py (2)
36-46: Allocate all wp.arrays on the viewer device (GPU/CPU) to prevent device-mismatch bugsCreate a local device handle from the viewer and pass device=self.device to every wp.array (colors, materials, axes, and per-frame transform arrays). This avoids GL backend crashes when Warp’s default device differs from the viewer device.
Apply this diff:
def __init__(self, viewer): - self.viewer = viewer + self.viewer = viewer + self.device = viewer.device # self.colors and materials per instance - self.col_sphere = wp.array([wp.vec3(0.9, 0.1, 0.1)], dtype=wp.vec3) - self.col_box = wp.array([wp.vec3(0.1, 0.9, 0.1)], dtype=wp.vec3) - self.col_cone = wp.array([wp.vec3(0.1, 0.4, 0.9)], dtype=wp.vec3) - self.col_capsule = wp.array([wp.vec3(0.9, 0.9, 0.1)], dtype=wp.vec3) - self.col_cylinder = wp.array([wp.vec3(0.8, 0.5, 0.2)], dtype=wp.vec3) - self.col_plane = wp.array([wp.vec3(0.125, 0.125, 0.15)], dtype=wp.vec3) + self.col_sphere = wp.array([wp.vec3(0.9, 0.1, 0.1)], dtype=wp.vec3, device=self.device) + self.col_box = wp.array([wp.vec3(0.1, 0.9, 0.1)], dtype=wp.vec3, device=self.device) + self.col_cone = wp.array([wp.vec3(0.1, 0.4, 0.9)], dtype=wp.vec3, device=self.device) + self.col_capsule = wp.array([wp.vec3(0.9, 0.9, 0.1)], dtype=wp.vec3, device=self.device) + self.col_cylinder = wp.array([wp.vec3(0.8, 0.5, 0.2)], dtype=wp.vec3, device=self.device) + self.col_plane = wp.array([wp.vec3(0.125, 0.125, 0.15)], dtype=wp.vec3, device=self.device) # material = (metallic, roughness, checker, unused) - self.mat_default = wp.array([wp.vec4(0.0, 0.7, 0.0, 0.0)], dtype=wp.vec4) - self.mat_plane = wp.array([wp.vec4(0.5, 0.5, 1.0, 0.0)], dtype=wp.vec4) + self.mat_default = wp.array([wp.vec4(0.0, 0.7, 0.0, 0.0)], dtype=wp.vec4, device=self.device) + self.mat_plane = wp.array([wp.vec4(0.5, 0.5, 1.0, 0.0)], dtype=wp.vec4, device=self.device) # Demonstrate log_lines() with animated debug/visualization lines axis_eps = 0.01 axis_length = 2.0 - self.axes_begins = wp.array( + self.axes_begins = wp.array( [ wp.vec3(0.0, 0.0, axis_eps), # X axis start wp.vec3(0.0, 0.0, axis_eps), # Y axis start wp.vec3(0.0, 0.0, axis_eps), # Z axis start ], - dtype=wp.vec3, + dtype=wp.vec3, + device=self.device, ) - self.axes_ends = wp.array( + self.axes_ends = wp.array( [ wp.vec3(axis_length, 0.0, axis_eps), # X axis end wp.vec3(0.0, axis_length, axis_eps), # Y axis end wp.vec3(0.0, 0.0, axis_length + axis_eps), # Z axis end ], - dtype=wp.vec3, + dtype=wp.vec3, + device=self.device, ) - self.axes_colors = wp.array( + self.axes_colors = wp.array( [ wp.vec3(1.0, 0.0, 0.0), # Red X wp.vec3(0.0, 1.0, 0.0), # Green Y wp.vec3(0.0, 0.0, 1.0), # Blue Z ], - dtype=wp.vec3, + dtype=wp.vec3, + device=self.device, )And in render (per-frame transform arrays):
- x_sphere_anim = wp.array([wp.transform(sphere_pos, qy_slow)], dtype=wp.transform) + x_sphere_anim = wp.array([wp.transform(sphere_pos, qy_slow)], dtype=wp.transform, device=self.device) - x_box_anim = wp.array([wp.transform([0.0, base_left, base_height], qx_slow)], dtype=wp.transform) + x_box_anim = wp.array([wp.transform([0.0, base_left, base_height], qx_slow)], dtype=wp.transform, device=self.device) - x_cone_anim = wp.array([wp.transform([0.0, base_left, base_height], qy_slow)], dtype=wp.transform) + x_cone_anim = wp.array([wp.transform([0.0, base_left, base_height], qy_slow)], dtype=wp.transform, device=self.device) - x_cyl_anim = wp.array([wp.transform([0.0, base_left, base_height], qz_slow)], dtype=wp.transform) + x_cyl_anim = wp.array([wp.transform([0.0, base_left, base_height], qz_slow)], dtype=wp.transform, device=self.device) - x_cap_anim = wp.array([wp.transform(capsule_pos, qy_slow)], dtype=wp.transform) + x_cap_anim = wp.array([wp.transform(capsule_pos, qy_slow)], dtype=wp.transform, device=self.device)Also applies to: 47-49, 54-61, 63-70, 72-79, 106-106, 111-111, 115-115, 119-119, 124-124
81-82: Remove stale string check for viewer type; print GL help in main insteadviewer is an object, so viewer == "gl" will never be True. Move the guidance print into main based on args.viewer.
Apply these diffs:
- if viewer == "gl": - print("Viewer running. WASD/Arrow keys to move, drag to orbit, scroll to zoom. Close window to exit.")if __name__ == "__main__": # Parse arguments and initialize viewer viewer, args = newton.examples.init() - # Create viewer and run + # Print GL-only help when using the GL viewer backend + if getattr(args, "viewer", None) == "gl": + print("Viewer running. WASD/Arrow keys to move, drag to orbit, scroll to zoom. Close window to exit.") + + # Create viewer and run example = Example(viewer) newton.examples.run(example)Also applies to: 189-196
🧹 Nitpick comments (5)
newton/tests/test_examples.py (1)
179-182: Reduce noisy logs: print stderr only on failure (optional)Currently prints stderr even on success, which can spam CI logs. Consider printing stderr/stdout only when returncode != 0.
Apply this diff:
- # print any error messages (e.g.: module not found) - if result.stderr != "": - print(result.stderr) + # Print process output on failure to aid debugging + if result.returncode != 0: + if result.stdout: + print(result.stdout) + if result.stderr: + print(result.stderr)newton/examples/__init__.py (1)
122-134: Docstring parameter mismatch (nit): clarify the ‘parser’ argumentThe function expects an argparse.ArgumentParser (or None) and internally calls parse_known_args, but the docstring says “Parsed arguments.” Recommend updating the docstring for accuracy.
Apply this diff:
- Args: - parser: Parsed arguments from argparse (should include arguments from - create_parser()) + Args: + parser: An argparse.ArgumentParser (or None). If None, a base parser from + create_parser() is constructed and parse_known_args() is used.newton/examples/basic/example_basic_viewer.py (3)
178-178: Typo in log_lines nameUse a clearer identifier without “self.” in the literal string.
- self.viewer.log_lines("coordinate_self.axes", self.axes_begins, self.axes_ends, self.axes_colors) + self.viewer.log_lines("coordinate_axes", self.axes_begins, self.axes_ends, self.axes_colors)
89-124: Optional: Avoid per-frame allocations by reusing xform arraysAllocate one-element wp.arrays for each x_*_anim in init and update their element each frame instead of constructing new arrays. This reduces Python/allocator overhead and GPU allocations.
Example outline (outside current ranges):
- In init: create x_sphere_anim = wp.zeros(1, dtype=wp.transform, device=self.device) for each.
- In render(): write x_sphere_anim[0] = wp.transform(...).
185-186: Implement a tiny smoke test for test harnesses that call Example.test()If newton/tests/test_examples.py expects examples to expose a test() entrypoint, calling render once is usually sufficient and works with ViewerNull.
Apply this diff:
- def test(self): - pass + def test(self): + # Single render step for CI smoke testing (works with ViewerNull) + self.render()Please confirm how test_examples.py discovers and executes example tests; if it expects a different contract, I can adjust accordingly.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
newton/_src/viewer/gl/opengl.py(1 hunks)newton/_src/viewer/viewer_gl.py(9 hunks)newton/examples/__init__.py(3 hunks)newton/examples/basic/example_basic_pendulum.py(1 hunks)newton/examples/basic/example_basic_urdf.py(1 hunks)newton/examples/basic/example_basic_viewer.py(5 hunks)newton/tests/test_examples.py(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/examples/basic/example_basic_urdf.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-18T15:56:26.540Z
Learnt from: adenzler-nvidia
PR: newton-physics/newton#552
File: newton/_src/solvers/mujoco/solver_mujoco.py:0-0
Timestamp: 2025-08-18T15:56:26.540Z
Learning: In Newton's MuJoCo solver, when transforming joint axes from Newton's internal frame to MuJoCo's expected frame, use wp.quat_rotate(joint_rot, axis) not wp.quat_rotate_inv(joint_rot, axis). The joint_rot represents rotation from joint-local to body frame, so forward rotation is correct.
Applied to files:
newton/examples/basic/example_basic_pendulum.py
🧬 Code Graph Analysis (6)
newton/_src/viewer/gl/opengl.py (1)
newton/_src/viewer/viewer_gl.py (2)
vsync(310-312)vsync(315-317)
newton/tests/test_examples.py (1)
newton/tests/unittest_utils.py (1)
sanitize_identifier(271-278)
newton/examples/__init__.py (2)
newton/tests/test_examples.py (1)
run(92-195)newton/tests/test_examples_new.py (1)
run(65-113)
newton/examples/basic/example_basic_pendulum.py (5)
newton/examples/basic/example_basic_urdf.py (5)
Example(35-132)capture(96-102)simulate(104-115)step(117-123)render(128-132)newton/_src/sim/builder.py (2)
ModelBuilder(68-4112)add_ground_plane(2201-2221)newton/_src/viewer/viewer.py (7)
finalize(401-409)set_model(66-74)apply_forces(367-368)begin_frame(76-77)log_state(79-100)log_contacts(102-163)end_frame(371-372)newton/_src/viewer/viewer_gl.py (5)
set_model(94-100)apply_forces(262-264)begin_frame(243-244)log_state(201-208)end_frame(246-260)newton/examples/__init__.py (1)
init(122-161)
newton/_src/viewer/viewer_gl.py (4)
newton/_src/viewer/viewer.py (2)
set_model(66-74)begin_frame(76-77)newton/_src/viewer/viewer_usd.py (1)
begin_frame(243-252)newton/_src/viewer/viewer_null.py (1)
begin_frame(46-47)newton/_src/viewer/gl/opengl.py (2)
get_vsync(957-963)set_vsync(949-955)
newton/examples/basic/example_basic_viewer.py (3)
newton/_src/viewer/viewer_gl.py (3)
begin_frame(243-244)log_lines(138-190)end_frame(246-260)newton/_src/viewer/viewer.py (4)
begin_frame(76-77)log_shapes(165-246)log_lines(351-352)end_frame(371-372)newton/examples/__init__.py (2)
init(122-161)run(36-44)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (20)
newton/_src/viewer/gl/opengl.py (1)
949-964: Runtime vsync control added — looks goodThe new set_vsync/get_vsync API aligns with ViewerGL’s property and leverages pyglet’s window hooks directly. Keeps initial construction behavior intact while enabling runtime toggles.
newton/tests/test_examples.py (2)
81-82: New test toggleuse_vieweris a good additionThis parameter cleanly separates legacy stage-path mode from the new viewer-driven path without breaking existing tests.
204-220: Viewer-mode smoke tests added — solid coverageThe basic example trio (pendulum, urdf, viewer) exercises the new viewer-oriented flow and USD/null switching. Good addition for preventing regressions in the example stack.
newton/examples/__init__.py (4)
36-45: run(example) loop matches new viewer lifecycleSteps and renders under timers and ensures viewer closure. Works for GL (is_running()) and aligns with USD/Null flows assuming they implement the same predicate.
50-57: Good deprecation path for compute_env_offsetsClear warning with replacement guidance to builder.replicate(), consistent with migration notes for warp.sim users.
97-120: Parser baseline is sensible and minimalCommon knobs (device, viewer, output-path, num-frames) are sufficient to bootstrap examples and tests. add_help=False makes it safe as a parent parser.
149-161: Viewer creation paths are sound; USD requires output_pathGL/USD/Null routing is correct and validates USD output path upfront. Returning (viewer, args) simplifies example entrypoints and test harness usage.
newton/examples/basic/example_basic_pendulum.py (3)
78-89: Solver wiring and initial FKMuJoCo solver is instantiated correctly. Calling eval_fk ensures state_0 is coherent for non-MuJoCo solvers; harmless here and keeps parity with other examples.
92-112: CUDA graph capture and substep loop look correctCapture-once and launch in step() match common patterns. The per-substep sequence (clear forces → apply viewer forces → collide → solver.step → swap) is in the right order.
124-139: Entry point integrates with new examples.init()/run() APIMain uses the shared init/run wiring. This keeps the example runnable both via CLI and the test harness.
newton/_src/viewer/viewer_gl.py (8)
34-41: Constructor refactor: defers model wiring and exposes vsync controlNew signature and RendererGL wiring look good. Deferring model setup enables model-less UI/workflows.
244-246: begin_frame delegates to base — goodEnsures ViewerBase bookkeeping (e.g., time) runs consistently across backends.
551-599: UI: guard model-dependent panelsConditionally showing “Model Information” and visualization toggles only when a model exists avoids None dereferences. Good defensive UI.
608-612: UI vsync toggle correctly routes to rendererThe checkbox uses the new ViewerGL.vsync property which calls RendererGL.set_vsync/get_vsync. Matches opengl.py additions.
707-717: Stats overlay: model metrics correctly gatedPrevents accessing model counters when no model is set.
734-736: Selection panel defaults to closed (sensible)Keeps UI compact by default; users can expand when needed.
395-397: Picking guard prevents null-state useRight-click picking only when a state was logged this frame avoids undefined ray/material data paths.
309-318: Public vsync property is simple and clearProperty accessors map 1:1 to renderer methods. No local state duplication — good.
newton/examples/basic/example_basic_viewer.py (2)
32-33: Good change: centralize viewer init/run via newton.examplesImporting newton.examples keeps example bootstrap consistent with the shared init/run helpers.
84-84: Time management and frame sequencing look correctInternal time accumulator with begin_frame(self.time) and per-frame increment is clean and consistent.
Also applies to: 92-92, 100-103, 183-183
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
newton/examples/basic/example_basic_pendulum.py (1)
56-70: Correct Warp API usage for transforms.Using wp.transform with wp.quat_identity() for parent/child frames is correct and resolves the earlier ‘wp.transformf’ issue noted in prior reviews.
🧹 Nitpick comments (10)
docs/images/examples/.gitattributes (1)
1-1: Broaden LFS patterns to cover .jpeg and uppercase variants.Some systems or tools may emit .jpeg or uppercase .JPG/.JPEG. Adding these prevents large binaries from slipping outside LFS.
Apply this diff to extend coverage:
*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.JPG filter=lfs diff=lfs merge=lfs -text +*.JPEG filter=lfs diff=lfs merge=lfs -textdocs/images/examples/resize.sh (1)
1-2: Add shebang and make robust when no PNGs are present (ShellCheck SC2148).Without a shebang, shells vary. Also avoid iterating the literal pattern when there are no matches.
Apply this diff:
-# resize all pngs in a directory by 2x in dimension + convert to jpg -for f in *.png; do ffmpeg -y -i "$f" -vf "scale=iw/2:ih/2" -q:v 3 "${f%.png}.jpg"; done +#!/usr/bin/env bash +# resize all pngs in a directory by 2x in dimension + convert to jpg +set -euo pipefail +shopt -s nullglob +for f in *.png; do + ffmpeg -y -i "$f" -vf "scale=iw/2:ih/2" -q:v 3 "${f%.png}.jpg" +donenewton/examples/__init__.py (3)
36-45: Guarantee viewer.close() runs even if step/render raises.Wrap the loop in try/finally to ensure proper cleanup.
Apply this diff:
-def run(example): - while example.viewer.is_running(): - with wp.ScopedTimer("step"): - example.step() - - with wp.ScopedTimer("render"): - example.render() - - example.viewer.close() +def run(example): + try: + while example.viewer.is_running(): + with wp.ScopedTimer("step"): + example.step() + with wp.ScopedTimer("render"): + example.render() + finally: + example.viewer.close()
50-57: Optional guard for invalid num_envs to avoid min/max on empty arrays.If num_envs <= 0, np.min/np.max will raise. Consider a clearer error.
Apply this diff:
warnings.warn( "compute_env_offsets is deprecated and will be removed in a future version. Use the builder.replicate() function instead.", stacklevel=2, ) + if num_envs <= 0: + raise ValueError("num_envs must be > 0")
122-134: Fix docstring: ‘parser’ is an ArgumentParser (or None), not parsed args.The current docstring is misleading.
Apply this diff:
-def init(parser=None): - """Initialize Newton example components from parsed arguments. - - Args: - parser: Parsed arguments from argparse (should include arguments from - create_parser()) +def init(parser=None): + """Initialize Newton example components from CLI arguments. + + Args: + parser: An argparse.ArgumentParser to parse arguments (if None, a default + parser from create_parser() is used)newton/examples/basic/example_basic_pendulum.py (1)
100-112: Minor: consider a scoped timer for simulate() for consistent profiling.Optional, but adding wp.ScopedTimer around the substep loop aligns with the timers used in newton.examples.run.
Apply this diff:
- def simulate(self): - for _ in range(self.sim_substeps): + def simulate(self): + with wp.ScopedTimer("simulate"): + for _ in range(self.sim_substeps): self.state_0.clear_forces() # apply forces to the model self.viewer.apply_forces(self.state_0) self.contacts = self.model.collide(self.state_0) self.solver.step(self.state_0, self.state_1, self.control, self.contacts, self.sim_dt) # swap states self.state_0, self.state_1 = self.state_1, self.state_0README.md (4)
63-63: Grammar: “command-line”Use “command-line” (or “CLI”) instead of “common line arguments”.
Apply this change:
-The examples support the following common line arguments: +The examples support the following common command-line arguments:
67-69: Headless environments: defaultglmay failDefaulting to
glis fine locally, but can fail in headless CI/servers. A short note will prevent confusion.Append after the table:
-| `--output-path` | Output path for USD files (required if `--viewer usd` is used). | `None` | +| `--output-path` | Output path for USD files (required if `--viewer usd` is used). | `None` | + +Tip: On headless systems, prefer `--viewer usd` (with `--output-path`) or `--viewer null` instead of the default `gl`.
79-81: Clarify “uv” and link to projectNice inclusion. Consider linking to uv for discoverability.
- # With uv - uv run python -m newton.examples basic_pendulum + # With uv (https://github.com/astral-sh/uv) + uv run python -m newton.examples basic_pendulum
65-71: Add note about ReRun SDK requirementThe
rerunviewer backend is fully supported (ViewerReruninnewton/_src/viewer/viewer_rerun.pywill raise anImportErrorwith “Install with: pip install rerun-sdk” if the SDK is missing). Please append the following under the argument table in README.md (around lines 65–71):Some examples may add additional arguments (see their respective source files for details). +Note: +- The `rerun` viewer requires the ReRun SDK to be installed (e.g., `pip install rerun-sdk`).
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
docs/images/examples/example_basic_pendulum.jpgis excluded by!**/*.jpgdocs/images/examples/example_basic_urdf.jpgis excluded by!**/*.jpgdocs/images/examples/example_basic_viewer.jpgis excluded by!**/*.jpg
📒 Files selected for processing (6)
README.md(1 hunks)docs/images/examples/.gitattributes(1 hunks)docs/images/examples/resize.bat(1 hunks)docs/images/examples/resize.sh(1 hunks)newton/examples/__init__.py(3 hunks)newton/examples/basic/example_basic_pendulum.py(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-18T15:56:26.540Z
Learnt from: adenzler-nvidia
PR: newton-physics/newton#552
File: newton/_src/solvers/mujoco/solver_mujoco.py:0-0
Timestamp: 2025-08-18T15:56:26.540Z
Learning: In Newton's MuJoCo solver, when transforming joint axes from Newton's internal frame to MuJoCo's expected frame, use wp.quat_rotate(joint_rot, axis) not wp.quat_rotate_inv(joint_rot, axis). The joint_rot represents rotation from joint-local to body frame, so forward rotation is correct.
Applied to files:
newton/examples/basic/example_basic_pendulum.py
🪛 Shellcheck (0.10.0)
docs/images/examples/resize.sh
[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.
(SC2148)
🪛 LanguageTool
README.md
[grammar] ~67-~67: There might be a mistake here.
Context: ...(no viewer). | gl | | --device | Compute devi...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ... | None (default Warp device) | | --num-frames | Number of fr...
(QB_NEW_EN)
[grammar] ~69-~69: There might be a mistake here.
Context: ... | 100 | | --output-path | Output path ...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (7)
newton/examples/__init__.py (1)
97-120: CLI scaffolding looks good.create_parser covers device/viewer/output-path/num-frames with sensible defaults and choices.
newton/examples/basic/example_basic_pendulum.py (1)
92-99: CUDA graph capture path is clean and safely falls back on CPU.Gating on wp.get_device().is_cuda and storing capture.graph is appropriate; fallback to None is clear.
README.md (5)
29-31: Good addition: Examples navigation headerThe new Examples section is a useful entry point and aligns with the new CLI runner.
29-60: Overall: Examples table and commands look coherent with the PR intentThe examples and commands are cohesive and provide a good quick-start path. Once the verification items above are confirmed, this section is in good shape.
33-47: All linked assets verifiedThe images and example scripts referenced in README.md (lines 33–47) are present in the PR; no broken links detected.
49-57: Example CLI entry names verifiedThe
example_mapinnewton/examples/__init__.pyincludes all three commands used in the README:
- basic_pendulum → newton.examples.basic.example_basic_pendulum
- basic_urdf → newton.examples.basic.example_basic_urdf
- basic_viewer → newton.examples.basic.example_basic_viewer
No changes needed.
83-89: No changes needed for basic_viewer example flagsThe
basic_viewerexample (newton/examples/basic/example_basic_viewer.py) usesnewton.examples.init(), which under the hood defines and parses the following flags innewton/examples/__init__.py:
--device--viewer(choices: “gl”, “usd”, “null”)--output-path(required when--viewer usd)--num-framesVerified:
• The README’s USD example passes both--viewer usdand--output-path, satisfying the requirement in init.
• Thebasic_viewerexample accepts and parses--viewer,--device, and--num-framesas documented.
• No errors are raised and the flags propagate correctly into the viewer construction.Conclusion: the commands in README.md are correct. No updates required.
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
docs/images/examples/resize.bat (1)
7-7: Quality setting inconsistent with the Bash scriptWindows uses -q:v 2 while the Bash script uses -q:v 3, leading to different file sizes/quality. Pick one value for consistency; the diff above sets both to 3.
README.md (1)
68-74: Clarify --num-frames applies to all viewers, not just USDAlign with parser behavior and prior discussion.
-| `--num-frames` | Number of frames to simulate (for USD output). | `100` | +| `--num-frames` | Number of frames to simulate. | `100` |newton/examples/__init__.py (1)
164-199: CLI entrypoint is clear and robust; sys.argv rewriting is correctThe argv rewrite keeps a list and preserves argv[0] as the target module, fixing the prior tuple bug reported earlier.
newton/examples/basic/example_basic_pendulum.py (1)
59-72: Correct Warp API usage for transforms and quaternionsGood fix replacing wp.transformf(...) with wp.transform(...), and using wp.quat_identity(). This matches usage elsewhere (e.g., URDF example) and avoids runtime AttributeError.
🧹 Nitpick comments (11)
docs/images/examples/.gitattributes (1)
1-1: Consider tracking PNGs (and JPEG aliases) with LFS as wellIf you plan to commit source PNGs alongside the generated JPGs, add PNG/JPEG aliases to avoid large blobs in Git history. Keeping this file scoped to docs/images/examples is fine.
Apply:
*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -textFollow-up: Confirm whether PNGs in this folder will be committed or only used locally as inputs. If they’re not committed, the current rule is sufficient.
docs/images/examples/resize.sh (1)
1-2: Add a shebang, fix the comment vs. behavior mismatch, and harden the loop
- The comment says “2x” but the filter halves the dimensions.
- No shebang; ShellCheck SC2148 flags target shell unknown.
- If no PNGs exist, the glob can pass a literal “*.png” to ffmpeg. Enable nullglob.
Apply:
-# resize all pngs in a directory by 2x in dimension, crop to square, + convert to jpg -for f in *.png; do ffmpeg -y -i "$f" -vf "crop=min(iw\,ih):min(iw\,ih),scale=iw/2:ih/2,format=yuv420p" -update 1 -q:v 3 "${f%.png}.jpg"; done +#!/usr/bin/env bash +# resize all PNGs in a directory to half size, crop to square, and convert to JPG +set -euo pipefail +shopt -s nullglob + +for f in *.png; do + ffmpeg -y -i "$f" -vf "crop=min(iw\,ih):min(iw\,ih),scale=iw/2:ih/2,format=yuv420p" -q:v 3 "${f%.png}.jpg" +doneNotes:
- Removed -update 1 since we’re writing unique outputs, not updating a single target.
- Consider aligning quality with the Windows script (see next comment) so both produce comparable outputs.
docs/images/examples/resize.bat (1)
3-9: Avoid temp files: combine crop+scale in a single FFmpeg call; align quality and drop -updateThe current two-pass flow is slower and temporarily writes PNGs. A single filter chain is simpler and faster. Also, -update is intended for single image sequences and is unnecessary here.
for %%f in (*.png) do ( echo Processing %%f... - REM First crop to square (keep as PNG), then resize to half and convert to JPG - ffmpeg -y -i "%%f" -vf "crop=min(iw\,ih):min(iw\,ih)" -update 1 "%%~nf_temp.png" - ffmpeg -y -i "%%~nf_temp.png" -vf "scale=iw/2:ih/2,format=yuv420p" -update 1 -q:v 2 "%%~nf.jpg" - del "%%~nf_temp.png" + REM Crop to square, resize to half, and convert to JPG in one step + ffmpeg -y -i "%%f" -vf "crop=min(iw,ih):min(iw,ih),scale=iw/2:ih/2,format=yuv420p" -q:v 3 "%%~nf.jpg" )Notes:
- The backslash before commas is not needed on Windows; keep the filter in double quotes.
- Set -q:v 3 to match the Bash script for parity across platforms (see next comment).
newton/examples/__main__.py (1)
6-9: LGTM; minor aliasing nit to avoid any ambiguityThis is correct and concise. As a tiny readability tweak, alias the import to make it explicit you’re calling the function exported by the package, not a submodule.
-from . import main +from . import main as _examples_main if __name__ == "__main__": - main() + _examples_main()README.md (1)
66-66: Grammar: “command-line arguments”Tweak phrasing.
-The examples support the following common line arguments: +The examples support the following common command-line arguments:newton/examples/__init__.py (6)
36-45: Close the viewer even if step/render raises: wrap loop in try/finallyPrevents resource leaks when exceptions occur inside the loop.
Apply this diff:
-def run(example): - while example.viewer.is_running(): - with wp.ScopedTimer("step"): - example.step() - - with wp.ScopedTimer("render"): - example.render() - - example.viewer.close() +def run(example): + try: + while example.viewer.is_running(): + with wp.ScopedTimer("step"): + example.step() + + with wp.ScopedTimer("render"): + example.render() + finally: + example.viewer.close()
50-56: Use DeprecationWarning for deprecationsUse a specific warning category so downstream can filter appropriately.
Apply this diff:
- warnings.warn( - "compute_env_offsets is deprecated and will be removed in a future version. Use the builder.replicate() function instead.", - stacklevel=2, - ) + warnings.warn( + "compute_env_offsets is deprecated and will be removed in a future version. Use the builder.replicate() function instead.", + category=DeprecationWarning, + stacklevel=2, + )
58-64: Guard against num_envs <= 0 to avoid reductions on empty arraysnp.min/max on an empty env_offsets array will raise. Either validate or early-return zero rows.
Apply this diff:
- # compute positional offsets per environment + # compute positional offsets per environment + if num_envs <= 0: + return np.zeros((0, 3))
122-134: Docstring parameter description is misleading: this takes a parser, not parsed argsClarify that the function accepts an argparse.ArgumentParser (or None), not a parsed Namespace.
Apply this diff:
-def init(parser=None): - """Initialize Newton example components from parsed arguments. - - Args: - parser: Parsed arguments from argparse (should include arguments from - create_parser()) +def init(parser=None): + """Initialize Newton example components from parsed arguments. + + Args: + parser (argparse.ArgumentParser | None): Parent parser that includes + the common arguments from create_parser(). If None, a default + parser is created via create_parser().
169-175: Optional: allow dotted module names in addition to short aliasesThis lets users run python -m newton.examples newton.examples.basic.example_basic_pendulum without updating the map.
Apply this diff:
- # Set up sys.argv for the target script - target_module = example_map[example_name] + # Set up sys.argv for the target script + if "." in example_name: + target_module = example_name + else: + target_module = example_map[example_name]Also applies to: 192-198
205-205: Optional: export main in allUseful if other tooling wants to import and forward the CLI routinely.
Apply this diff:
-__all__ = ["create_parser", "init", "run"] +__all__ = ["create_parser", "init", "run", "main"]
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
docs/images/examples/example_basic_pendulum.jpgis excluded by!**/*.jpgdocs/images/examples/example_basic_urdf.jpgis excluded by!**/*.jpgdocs/images/examples/example_basic_viewer.jpgis excluded by!**/*.jpg
📒 Files selected for processing (10)
README.md(1 hunks)docs/images/examples/.gitattributes(1 hunks)docs/images/examples/resize.bat(1 hunks)docs/images/examples/resize.sh(1 hunks)newton/_src/sim/builder.py(1 hunks)newton/examples/__init__.py(3 hunks)newton/examples/__main__.py(1 hunks)newton/examples/basic/example_basic_pendulum.py(1 hunks)newton/examples/basic/example_basic_urdf.py(1 hunks)newton/examples/basic/example_basic_viewer.py(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- newton/examples/basic/example_basic_urdf.py
- newton/_src/sim/builder.py
- newton/examples/basic/example_basic_viewer.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-18T15:56:26.540Z
Learnt from: adenzler-nvidia
PR: newton-physics/newton#552
File: newton/_src/solvers/mujoco/solver_mujoco.py:0-0
Timestamp: 2025-08-18T15:56:26.540Z
Learning: In Newton's MuJoCo solver, when transforming joint axes from Newton's internal frame to MuJoCo's expected frame, use wp.quat_rotate(joint_rot, axis) not wp.quat_rotate_inv(joint_rot, axis). The joint_rot represents rotation from joint-local to body frame, so forward rotation is correct.
Applied to files:
newton/examples/basic/example_basic_pendulum.py
🧬 Code Graph Analysis (3)
newton/examples/__main__.py (1)
newton/examples/__init__.py (1)
main(164-198)
newton/examples/basic/example_basic_pendulum.py (6)
newton/examples/basic/example_basic_urdf.py (5)
Example(37-134)capture(98-104)simulate(106-117)step(119-125)render(130-134)newton/_src/sim/builder.py (5)
ModelBuilder(67-4096)add_articulation(582-588)add_shape_box(2255-2292)add_joint_revolute(1061-1143)add_ground_plane(2200-2220)newton/_src/viewer/viewer.py (7)
finalize(401-409)set_model(66-74)apply_forces(367-368)begin_frame(76-77)log_state(79-100)log_contacts(102-163)end_frame(371-372)newton/_src/sim/model.py (3)
state(441-474)control(476-508)collide(510-564)newton/_src/viewer/viewer_gl.py (5)
set_model(94-100)apply_forces(262-264)begin_frame(243-244)log_state(201-208)end_frame(246-260)newton/examples/__init__.py (2)
init(122-161)run(36-44)
newton/examples/__init__.py (5)
newton/tests/test_examples.py (1)
run(92-195)newton/tests/test_examples_new.py (1)
run(65-113)newton/_src/viewer/viewer_gl.py (2)
is_running(297-299)close(305-307)newton/_src/viewer/viewer_usd.py (3)
is_running(258-262)close(264-267)ViewerUSD(14-317)newton/_src/viewer/viewer_null.py (3)
is_running(52-53)close(55-56)ViewerNull(22-69)
🪛 LanguageTool
README.md
[grammar] ~70-~70: There might be a mistake here.
Context: ...(no viewer). | gl | | --device | Compute devi...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ... | None (default Warp device) | | --num-frames | Number of fr...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ... | 100 | | --output-path | Output path ...
(QB_NEW_EN)
🪛 Shellcheck (0.10.0)
docs/images/examples/resize.sh
[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.
(SC2148)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (5)
README.md (1)
33-63: LGTM: Clear, actionable examples with image linksNice touch linking images and one-liner invocations; matches the -m entry-point behavior in newton/examples.
newton/examples/__init__.py (1)
97-120: Parser looks solid and self-containedGood defaults, explicit choices, and add_help=False for parent usage. No issues.
newton/examples/basic/example_basic_pendulum.py (3)
95-101: CUDA graph capture implementation is sensibleCapturing simulate() with ScopedCapture and reusing the graph on step() is a clean pattern; falls back to non-capture on CPU.
126-131: Render path aligns with viewer API expectationsbegin_frame/log_state/log_contacts/end_frame call order is consistent with the viewer backends and ensures Null/USD viewers advance frame counts for is_running().
134-141: Main flow integrates with newton.examples.init/run correctlyThe example is directly runnable via python -m newton.examples.basic_pendulum and respects CLI options parsed by init().
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
…into example-refactor
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
README.md (1)
105-105: Clarify --num-frames applies to all viewers, not just USD outputThis repeats a previously raised point; the flag controls simulation length across viewers.
Apply this diff:
-| `--num-frames` | Number of frames to simulate (for USD output). | `100` | +| `--num-frames` | Number of frames to simulate. | `100` |newton/tests/test_examples.py (1)
141-166: Fix UnboundLocalError: stage_path undefined when use_viewer=True and USD is unavailableIn the null-viewer branch, stage_path is never assigned but is used later for cleanup (Line 191). Initialize it to None.
Apply this diff:
else: # new-style example, setup viewer type and output path if USD_AVAILABLE: stage_path = os.path.join( os.path.dirname(__file__), f"outputs/{name}_{sanitize_identifier(device)}.usd" ) command.extend(["--viewer", "usd", "--output-path", stage_path]) else: command.extend(["--viewer", "null"]) + stage_path = Nonenewton/_src/viewer/viewer_gl.py (1)
95-103: Initialize a default Warp device when no model is set (prevents None device usage).Model-less flows call set_model(None) in init, leaving self.device=None. That can leak into GL resource creation (e.g., LinesGL) and allocations with an invalid device. Set a sensible default when model is None.
Apply this diff:
def set_model(self, model): super().set_model(model) + # Ensure we have a valid Warp device for model-less flows too + if model is None and self.device is None: + self.device = wp.get_device() + self.picking = Picking(model, pick_stiffness=10000.0, pick_damping=1000.0) self.wind = Wind(model) fb_w, fb_h = self.renderer.window.get_framebuffer_size() self.camera = Camera(width=fb_w, height=fb_h)
🧹 Nitpick comments (10)
newton/examples/cloth/example_cloth_bending.py (1)
129-133: Consider rendering contacts for debugging consistencyOther cloth examples log contact normals; adding them here helps with consistency and debugging.
Apply this diff:
def render(self): self.viewer.begin_frame(self.sim_time) self.viewer.log_state(self.state_0) + # Optional: visualize contact normals for consistency with other cloth examples + self.viewer.log_contacts(self.contacts, self.state_0) self.viewer.end_frame()newton/examples/cloth/example_cloth_style3d.py (2)
171-172: Remove placeholder test() or wire it into CIAn empty test method is dead code here.
Apply this diff:
- def test(self): - pass
46-50: Nit: fix comment typo “Grament” → “Garment”Minor readability improvement.
Apply this diff:
- # Grament + # GarmentREADME.md (1)
99-99: Use “command-line arguments” terminologyMinor wording improvement.
Apply this diff:
-The examples support the following common line arguments: +The examples support the following common command-line arguments:newton/_src/viewer/wind.py (3)
63-66: Guard against period <= 0 to avoid division by zero.If period is set programmatically to 0 (UI constrains to [1,30] but not guaranteed elsewhere),
time_phase = 2*pi*time/periodwill crash or produce NaNs.Apply this diff:
- time_phase = 2.0 * wp.pi * params.time / params.period + # Guard against zero/near-zero period + if params.period <= 1.0e-6: + return + time_phase = 2.0 * wp.pi * params.time / params.period
186-202: Always normalize direction; docstring promises normalization but wp.vec3 path skips it.The tuple/list path normalizes, but the “vector-like” branch copies values without normalization. This violates the contract and can scale forces unintentionally.
Apply this diff:
@direction.setter def direction(self, value): """Set wind direction (will be normalized).""" - if isinstance(value, (list, tuple)) and len(value) == 3: - # Normalize the direction vector - import math # noqa: PLC0415 - - length = math.sqrt(value[0] ** 2 + value[1] ** 2 + value[2] ** 2) - if length > 0: - self._wind_params_host.direction = wp.vec3(value[0] / length, value[1] / length, value[2] / length) - else: - self._wind_params_host.direction = wp.vec3(1.0, 0.0, 0.0) - elif hasattr(value, "__len__") and len(value) == 3: - # Handle wp.vec3 or similar - self._wind_params_host.direction = wp.vec3(float(value[0]), float(value[1]), float(value[2])) + if hasattr(value, "__len__") and len(value) == 3: + import math # noqa: PLC0415 + x, y, z = float(value[0]), float(value[1]), float(value[2]) + length = math.sqrt(x * x + y * y + z * z) + if length > 0.0: + self._wind_params_host.direction = wp.vec3(x / length, y / length, z / length) + else: + # Default to +X if zero vector is passed + self._wind_params_host.direction = wp.vec3(1.0, 0.0, 0.0) else: raise ValueError("Direction must be a 3-element vector")
55-58: Consider handling negative amplitudes explicitly or clamping at zero.The UI slider allows negative amplitudes but the kernel early-returns for amplitude <= 0, turning half the slider range into a no-op. Either clamp on set or document that only positive values have effect.
newton/examples/cloth/example_cloth_hanging.py (1)
190-195: Contacts logging is fine; default visibility gated by UI.Calling viewer.log_contacts even on cloth-only scenes is safe: the viewer-side option guards actual rendering and allocations.
newton/_src/viewer/viewer_gl.py (2)
229-236: Guard picking line rendering for models without bodies.On cloth-only scenes, state.body_q may be None. Accessing numpy() will raise. Add a defensive check.
Apply this diff:
- # Get the body's COM position - body_transforms = state.body_q.numpy() + # Get the body's COM position (guard cloth-only scenes) + if not hasattr(state, "body_q") or state.body_q is None: + self.log_lines("picking_line", None, None, None) + return + body_transforms = state.body_q.numpy()
169-177: Minor: verify wp.array.fill_ availability or use wp.fill().Using line_colors.fill_(color_vec) assumes Warp arrays expose fill_. If unsupported on some versions, prefer wp.fill(line_colors, color_vec) to avoid API discrepancies.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
docs/images/examples/example_cloth_bending.jpgis excluded by!**/*.jpgdocs/images/examples/example_cloth_hanging.jpgis excluded by!**/*.jpgdocs/images/examples/example_cloth_style3d.jpgis excluded by!**/*.jpg
📒 Files selected for processing (9)
README.md(1 hunks)newton/_src/solvers/style3d/linear_solver.py(1 hunks)newton/_src/viewer/viewer_gl.py(11 hunks)newton/_src/viewer/wind.py(1 hunks)newton/examples/__init__.py(3 hunks)newton/examples/cloth/example_cloth_bending.py(2 hunks)newton/examples/cloth/example_cloth_hanging.py(2 hunks)newton/examples/cloth/example_cloth_style3d.py(8 hunks)newton/tests/test_examples.py(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/examples/init.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
🧬 Code Graph Analysis (6)
newton/_src/viewer/wind.py (2)
newton/_src/sim/model.py (1)
state(441-474)newton/_src/sim/builder.py (1)
particle_count(506-507)
newton/_src/viewer/viewer_gl.py (4)
newton/_src/viewer/wind.py (13)
Wind(81-202)time(142-144)time(147-149)_apply_wind_force(117-132)update(104-115)amplitude(162-164)amplitude(167-169)period(152-154)period(157-159)frequency(172-174)frequency(177-179)direction(182-184)direction(187-202)newton/_src/viewer/viewer.py (4)
ViewerBase(33-649)set_model(66-74)begin_frame(76-77)update(411-420)newton/_src/viewer/gl/opengl.py (7)
RendererGL(689-1503)update(235-282)update(381-426)update(629-674)update(830-839)get_vsync(957-963)set_vsync(949-955)newton/_src/viewer/camera.py (1)
Camera(19-186)
newton/examples/cloth/example_cloth_style3d.py (6)
newton/examples/cloth/example_cloth_bending.py (5)
Example(36-132)capture(101-107)simulate(109-120)step(122-127)render(129-132)newton/_src/sim/builder.py (1)
add_ground_plane(2200-2220)newton/_src/sim/model.py (3)
state(441-474)control(476-508)collide(510-564)newton/_src/viewer/viewer_gl.py (5)
set_model(95-102)apply_forces(264-269)begin_frame(245-246)log_state(203-210)end_frame(248-262)newton/_src/viewer/viewer.py (1)
log_contacts(102-163)newton/examples/__init__.py (2)
init(126-167)run(36-44)
newton/tests/test_examples.py (1)
newton/tests/unittest_utils.py (1)
sanitize_identifier(271-278)
newton/examples/cloth/example_cloth_bending.py (4)
newton/_src/sim/model.py (2)
control(476-508)collide(510-564)newton/_src/viewer/viewer_gl.py (5)
set_model(95-102)apply_forces(264-269)begin_frame(245-246)log_state(203-210)end_frame(248-262)newton/examples/cloth/example_cloth_hanging.py (4)
capture(158-164)simulate(166-177)step(179-185)render(190-194)newton/examples/__init__.py (2)
init(126-167)run(36-44)
newton/examples/cloth/example_cloth_hanging.py (5)
newton/examples/cloth/example_cloth_bending.py (5)
Example(36-132)capture(101-107)simulate(109-120)step(122-127)render(129-132)newton/_src/sim/model.py (3)
state(441-474)control(476-508)collide(510-564)newton/_src/viewer/viewer_gl.py (5)
set_model(95-102)apply_forces(264-269)begin_frame(245-246)log_state(203-210)end_frame(248-262)newton/_src/viewer/viewer.py (1)
log_contacts(102-163)newton/examples/__init__.py (3)
create_parser(97-123)init(126-167)run(36-44)
🪛 LanguageTool
README.md
[grammar] ~103-~103: There might be a mistake here.
Context: ...(no viewer). | gl | | --device | Compute devi...
(QB_NEW_EN)
[grammar] ~104-~104: There might be a mistake here.
Context: ... | None (default Warp device) | | --num-frames | Number of fr...
(QB_NEW_EN)
[grammar] ~105-~105: There might be a mistake here.
Context: ... | 100 | | --output-path | Output path ...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
🔇 Additional comments (21)
newton/_src/solvers/style3d/linear_solver.py (1)
176-184: Add size equality check before low-level call & verify C function signatureTo prevent out-of-bounds access if
aandbdiffer in length, insert a precondition check just before the call intoruntime.core, and then confirm that both backends’ C functions accept the same 7 arguments in this order.File: newton/_src/solvers/style3d/linear_solver.py (around line 176)
Apply this diff:
- func( + if len(a) != len(b): + raise ValueError(f"array_inner requires arrays of equal length, got {len(a)} and {len(b)}") + + func( a.ptr, b.ptr, out_ptr, len(a), wp.types.type_size_in_bytes(a.dtype), wp.types.type_size_in_bytes(b.dtype), wp.types.type_length(a.dtype), )• Please manually verify that
wp_array_inner_float_hostandwp_array_inner_float_device(viaruntime.core) both expect these seven parameters in the same order by checking your C-extension headers or documentation.newton/examples/cloth/example_cloth_bending.py (3)
37-45: LGTM: clean viewer-centric timing and substepping setupThe refactor to explicit fps/frame_dt/sim_substeps/sim_dt is clear and aligns with other examples.
97-101: Bind model to viewer before capture — good orderingModel binding precedes graph capture; this ensures viewer-side resources (forces, picking, etc.) are initialized before simulate/capture runs.
135-143: Entry flow matches new shared runner — looks goodUsing newton.examples.init()/run() keeps the example consistent with the unified CLI and viewer backends.
newton/examples/cloth/example_cloth_style3d.py (5)
35-38: Even substeps for CUDA graph capture — good defensive noteDocumenting the even substeps requirement alongside the assignment improves clarity.
80-87: LGTM: rigid mesh as static collider with transformSupplying xform and separate body for the avatar mesh is correct for collision; matches builder APIs.
126-127: Explicit gravity assignment — good for cross-example consistencySetting gravity explicitly avoids relying on builder/model defaults.
145-151: Capture path is correct and isolated for CPU devicesConditionally capturing only on CUDA while keeping CPU path functional is the right tradeoff.
175-178: Good: include contacts in render for debuggingLogging both state and contacts keeps the visualization informative.
README.md (1)
112-126: Docs additions look aligned with new example flowThe new Examples section and usage commands match the shared CLI and viewer-based runner.
newton/tests/test_examples.py (3)
179-182: Printing stderr aids debugging flaky example runsGood addition; keeps CI logs actionable without failing the test prematurely.
204-220: Viewer-mode basic example registration — looks goodTests exercise the viewer path consistently across devices; options split between CPU/CUDA are sensible.
226-258: Cloth examples wired to viewer mode — consistent with refactorStyle3D’s reduced num_frames on CPU is a reasonable concession.
newton/examples/cloth/example_cloth_hanging.py (4)
40-59: Initialization flow and substep timing look good.The viewer-driven frame_dt and sim_substeps → sim_dt mapping is clear and consistent across solver types. Good separation between frame time and simulation substeps.
166-178: Force application ordering fits the solvers; dependency on viewer wind fix.The clear_forces → viewer.apply_forces → collide → solver.step loop is correct. Once wind writes into particle_f (see wind.py fix), this flow will integrate the wind as intended.
197-224: CLI wiring and viewer bootstrap LGTM.The CLI integrates cleanly with newton.examples.{create_parser,init,run}. Solver selection via --solver matches your branching.
62-74: Confirmed add_aniso_cloth_grid signature supports edge_kdThe
add_aniso_cloth_gridmethod innewton/_src/sim/style3d/builder_style3d.py(around line 376) defines bothedge_aniso_keandedge_kd, so popping onlyedge_keand passingedge_kdfromcommon_paramswill not produce any unexpected keyword-argument errors. No changes needed here.newton/_src/viewer/viewer_gl.py (4)
245-247: Good: begin_frame now respects the base viewer’s timing.Delegating to super().begin_frame(time) ensures consistent timing across viewers.
268-270: Wind integration is well-placed; update cadence decouples from sim.apply_forces() and _update() hooks are appropriate. Once wind writes to particle_f and drops dt scaling (see wind.py), the force will integrate correctly regardless of viewer frame rate.
Also applies to: 281-282
399-404: Nice: prevent picking when no cached state is available.The added guard avoids None derefs during early interaction.
615-664: UI sliders and toggles are cohesive; direction slider feeds normalized setter.Assuming wind.direction setter is fixed to always normalize (see wind.py), this UI produces predictable forces.
Signed-off-by: Gabriel Noya <mega.kaloca@gmail.com>
Description
Newton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this MR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)pre-commit run -aSummary by CodeRabbit
Migration Guide
Newton Examples Migration Guide
This guide explains how to migrate Newton examples from the old argument-parsing style to the new viewer-based pattern introduced in Newton 2025.
Overview
The new example format standardizes how Newton examples are structured, making them more consistent and easier to use. The key changes involve:
viewerobject instead of managing their own renderersnewton.examples.init()Newton Example Requirements
During migration please ensure your example follows these guidelines:
Functionality & Design
Technical Standards
Performance Requirements
Documentation & Testing
Step-by-Step Migration
1. Update Imports
Before:
After:
import newton.exampleswp.config.enable_backward = False2. Update Constructor Signature
Before:
After:
Key Changes:
viewerinstead of stage/output parametersnum_frames,use_cuda_graphparametersnum_substeps->sim_substepsanddt→sim_dtfor consistency3. Update Variable Names for Consistency
Before:
After:
Key Changes:
state0->state_0,state1→state_14. Replace Custom Renderer with Viewer
Before:
After:
5. Update Simulation Methods
Before:
After:
Key Changes:
simulate_substeps()→simulate()capture()method with comment "put graph capture into it's own function"self.graphinstead ofself.cuda_graphself.viewer.apply_forces(self.state_0)callself.contacts6. Update Step Method
Before:
After:
Key Changes:
self.graphinstead ofself.cuda_graphself.simulate()instead ofself.simulate_substeps()7. Add Required Methods
Although not yet widely used, having a
test()method is welcome and will be checked by the test suite in a follow up MR.8. Initialize Model at End of Constructor
Before:
After:
Key Changes:
self.viewer.set_model(self.model)self.capture()at the end9. Update Main Function
Before:
After (Simple):
After (With Custom Arguments):
Naming Conventions
Follow these naming conventions from the inline comments:
Individual objects: Use the name of the asset or just "robot" for individual articulations, not "articulation_builder"
Full scenes: Use "scene" for the entire set of environments
Updating Documentation
1. Add to newton.examples.init.py
Add your example to the
example_mapdictionary:2. Add Thumbnail to README.md
Add your example to the appropriate table in README.md:
And the corresponding command:
3. Generate Thumbnail Image
docs/images/examples/example_your_example.pngTesting Your Migration
After migration, test your example:
Direct execution:
Short name execution:
Different viewers:
Custom arguments (if any):
Add Unit Tests
Update test_examples.py with your example, pass
use_viewer=Trueto theadd_example_test()function:Common Issues
state_0notstate0)sim_substeps/sim_dtnotnum_substeps/dt3 Model Logging: Don't forget to call
self.viewer.log_state(self.state_0)to update the viewerself.viewer.apply_forces(self.state_0)in simulate if you want pickingself.viewer.log_contacts(self.contacts, self.state_0)in render if example has contactsExample Runner
There is now a new example run script that will take care of standard argument parsing, viewer initialization, etc:
Example Options
The example run script supports the following common line arguments:
--viewergl(OpenGL window),usd(USD file output),rerun(ReRun), ornull(no viewer).gl--devicecpu,cuda:0, etc.None(default Warp device)--num-frames100--output-path--viewer usdis used).NoneSome examples may add additional arguments (see their respective source files for details).
Example Usage
Complete Example: