-
Notifications
You must be signed in to change notification settings - Fork 477
wp.transform_set_translation() silently failing #1336
Copy link
Copy link
Description
wp.transform_set_translation() has no effect on a transformation constructed with wp.transform_identity()
See minimal repro and possible explanation below (generated by Claude but double-checked):
"""
Minimal reproduction: wp.transform_set_translation() silently fails on
objects returned by wp.transform_identity().
Root cause: wp.transform_identity() returns an instance of the internal
base class `transform_t` (i.e. transformation(dtype=float32)), NOT the
concrete `wp.transformf` subclass.
Warp's Python-side builtin dispatch (context.py:get_builtin_call_desc)
checks `issubclass(type(param), wp.transformf)` to decide how to pass
the argument to the C function:
- True -> BUILTIN_PREDEFINED -> ctypes.byref(param)
The C function modifies the original object in-place. Works.
- False -> BUILTIN_GENERIC -> ctypes.byref(wp.transformf(param))
A *temporary copy* is created, the C function modifies the
copy, the copy is discarded. The original is unchanged.
Silent no-op.
Since transform_t is the *parent* of transformf, issubclass(transform_t,
transformf) is False, so transform_identity() objects always take the
broken BUILTIN_GENERIC path.
Tested with Warp 1.13.0.dev0.
"""
import warp as wp
wp.init()
pos = wp.vec3f(1.0, 2.0, 3.0)
# ---- WORKING: constructed via wp.transformf() ----
t_good = wp.transformf()
print(f"wp.transformf() -> type: {type(t_good).__name__:12s} is wp.transformf: {type(t_good) is wp.transformf}")
wp.transform_set_translation(t_good, pos)
print(f" after set_translation: {t_good}")
assert t_good[0] == 1.0 and t_good[1] == 2.0 and t_good[2] == 3.0, "FAIL"
print(" OK\n")
# ---- BROKEN: constructed via wp.transform_identity() ----
t_bad = wp.transform_identity()
print(f"wp.transform_identity() -> type: {type(t_bad).__name__:12s} is wp.transformf: {type(t_bad) is wp.transformf}")
wp.transform_set_translation(t_bad, pos)
print(f" after set_translation: {t_bad}")
assert t_bad[0] == 0.0 and t_bad[1] == 0.0 and t_bad[2] == 0.0, \
"If this assertion fails, the bug has been fixed!"
print(" BUG: transform unchanged (silent no-op)\n")
# ---- WHY: type hierarchy ----
print("Type hierarchy:")
print(f" type(wp.transformf()) = {type(t_good)}")
print(f" type(wp.transform_identity()) = {type(t_bad)}")
print(f" issubclass(type(t_good), wp.transformf) = {issubclass(type(t_good), wp.transformf)}")
print(f" issubclass(type(t_bad), wp.transformf) = {issubclass(type(t_bad), wp.transformf)}")
print(f" wp.transformf base: {wp.transformf.__bases__}")
print(f" transform_identity() type base: {type(t_bad).__bases__}")
print(f" Same parent class? {wp.transformf.__bases__[0] is type(t_bad).__bases__[0]}")
# ---- WORKAROUND: direct indexing ----
print("\nWorkaround (direct indexing):")
t_bad[0], t_bad[1], t_bad[2] = pos[0], pos[1], pos[2]
print(f" after t_bad[0:3] = pos: {t_bad}")
assert t_bad[0] == 1.0
print(" OK")
For me this outputs:
wp.transformf() -> type: transformf is wp.transformf: True
after set_translation: [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0]
OK
wp.transform_identity() -> type: transform_t is wp.transformf: False
after set_translation: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
BUG: transform unchanged (silent no-op)
Type hierarchy:
type(wp.transformf()) = <class 'warp._src.types.transformf'>
type(wp.transform_identity()) = <class 'warp._src.types.transformation.<locals>.transform_t'>
issubclass(type(t_good), wp.transformf) = True
issubclass(type(t_bad), wp.transformf) = False
wp.transformf base: (<class 'warp._src.types.transformation.<locals>.transform_t'>,)
transform_identity() type base: (<class 'warp._src.types.vector.<locals>.vec_t'>,)
Same parent class? False
Workaround (direct indexing):
after t_bad[0:3] = pos: [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0]
OK
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working