transform-tree 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. transform_tree-0.0.6/Cargo.toml +120 -0
  2. {transform_tree-0.0.4/crates/tf_tree_core → transform_tree-0.0.6}/NOTICE +2 -1
  3. transform_tree-0.0.6/PKG-INFO +169 -0
  4. transform_tree-0.0.6/README.md +148 -0
  5. transform_tree-0.0.6/crates/tf_tree/Cargo.toml +74 -0
  6. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/NOTICE +2 -1
  7. transform_tree-0.0.6/crates/tf_tree/README.md +104 -0
  8. transform_tree-0.0.6/crates/tf_tree/examples/control_loop.rs +207 -0
  9. transform_tree-0.0.6/crates/tf_tree/examples/gen_domain_fixture.rs +94 -0
  10. transform_tree-0.0.6/crates/tf_tree/examples/two_processes.rs +174 -0
  11. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/src/bin/rendezvous_child.rs +165 -71
  12. transform_tree-0.0.6/crates/tf_tree/src/cache.rs +581 -0
  13. transform_tree-0.0.6/crates/tf_tree/src/cbor.rs +151 -0
  14. transform_tree-0.0.6/crates/tf_tree/src/frozen.rs +211 -0
  15. transform_tree-0.0.6/crates/tf_tree/src/lib.rs +204 -0
  16. transform_tree-0.0.6/crates/tf_tree/src/open.rs +1053 -0
  17. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/src/tree.rs +622 -1350
  18. transform_tree-0.0.6/crates/tf_tree/src/unstable.rs +52 -0
  19. transform_tree-0.0.6/crates/tf_tree/tests/await_frames.rs +134 -0
  20. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/batch.rs +54 -181
  21. transform_tree-0.0.6/crates/tf_tree/tests/batch_phases.rs +845 -0
  22. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/behavior.rs +54 -144
  23. transform_tree-0.0.6/crates/tf_tree/tests/clock_offset.rs +308 -0
  24. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/common/mod.rs +9 -19
  25. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/construction.rs +17 -66
  26. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/counters.rs +49 -110
  27. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/derivatives.rs +48 -228
  28. transform_tree-0.0.6/crates/tf_tree/tests/enumeration.rs +112 -0
  29. transform_tree-0.0.6/crates/tf_tree/tests/error_payloads.rs +206 -0
  30. transform_tree-0.0.6/crates/tf_tree/tests/feature_gates.rs +119 -0
  31. transform_tree-0.0.6/crates/tf_tree/tests/frozen.rs +516 -0
  32. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/lookup.rs +132 -15
  33. transform_tree-0.0.6/crates/tf_tree/tests/math_reexports.rs +92 -0
  34. transform_tree-0.0.6/crates/tf_tree/tests/owned_writer.rs +240 -0
  35. transform_tree-0.0.6/crates/tf_tree/tests/plan_cache_identity.rs +259 -0
  36. transform_tree-0.0.6/crates/tf_tree/tests/rendezvous.rs +4005 -0
  37. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/tests/tsan.rs +5 -30
  38. transform_tree-0.0.6/crates/tf_tree/tests/wide_stamps.rs +172 -0
  39. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/NOTICE +2 -1
  40. transform_tree-0.0.6/crates/tf_tree_arena/README.md +45 -0
  41. transform_tree-0.0.6/crates/tf_tree_arena/src/check.rs +463 -0
  42. transform_tree-0.0.6/crates/tf_tree_arena/src/frozen.rs +939 -0
  43. transform_tree-0.0.6/crates/tf_tree_arena/src/header.rs +253 -0
  44. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/src/heap.rs +41 -137
  45. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/src/layout.rs +108 -145
  46. transform_tree-0.0.6/crates/tf_tree_arena/src/lib.rs +58 -0
  47. transform_tree-0.0.6/crates/tf_tree_arena/src/mapped.rs +603 -0
  48. transform_tree-0.0.6/crates/tf_tree_arena/src/render_test.rs +67 -0
  49. transform_tree-0.0.6/crates/tf_tree_arena/tests/heap_alignment.rs +99 -0
  50. transform_tree-0.0.6/crates/tf_tree_bridge/Cargo.toml +29 -0
  51. transform_tree-0.0.6/crates/tf_tree_bridge/examples/offer_cost.rs +180 -0
  52. transform_tree-0.0.6/crates/tf_tree_bridge/src/authority.rs +347 -0
  53. transform_tree-0.0.6/crates/tf_tree_bridge/src/clock.rs +738 -0
  54. transform_tree-0.0.6/crates/tf_tree_bridge/src/config.rs +1435 -0
  55. transform_tree-0.0.6/crates/tf_tree_bridge/src/discover.rs +415 -0
  56. transform_tree-0.0.6/crates/tf_tree_bridge/src/edgeindex.rs +280 -0
  57. transform_tree-0.0.6/crates/tf_tree_bridge/src/edgemap.rs +34 -0
  58. transform_tree-0.0.6/crates/tf_tree_bridge/src/ingest.rs +2066 -0
  59. transform_tree-0.0.6/crates/tf_tree_bridge/src/interner.rs +142 -0
  60. transform_tree-0.0.6/crates/tf_tree_bridge/src/lib.rs +213 -0
  61. transform_tree-0.0.6/crates/tf_tree_bridge/src/names.rs +207 -0
  62. transform_tree-0.0.6/crates/tf_tree_bridge/src/statics.rs +483 -0
  63. transform_tree-0.0.6/crates/tf_tree_bridge/src/stats.rs +117 -0
  64. transform_tree-0.0.6/crates/tf_tree_bridge/tests/steady_state_alloc.rs +191 -0
  65. transform_tree-0.0.6/crates/tf_tree_core/Cargo.toml +63 -0
  66. {transform_tree-0.0.4 → transform_tree-0.0.6/crates/tf_tree_core}/NOTICE +2 -1
  67. transform_tree-0.0.6/crates/tf_tree_core/README.md +48 -0
  68. transform_tree-0.0.6/crates/tf_tree_core/src/arena_view.rs +532 -0
  69. transform_tree-0.0.6/crates/tf_tree_core/src/bench_probe.rs +22 -0
  70. transform_tree-0.0.6/crates/tf_tree_core/src/buffer.rs +304 -0
  71. transform_tree-0.0.6/crates/tf_tree_core/src/counters.rs +139 -0
  72. transform_tree-0.0.6/crates/tf_tree_core/src/crash.rs +114 -0
  73. transform_tree-0.0.6/crates/tf_tree_core/src/crash_tests.rs +529 -0
  74. transform_tree-0.0.6/crates/tf_tree_core/src/edge.rs +411 -0
  75. transform_tree-0.0.6/crates/tf_tree_core/src/error.rs +453 -0
  76. transform_tree-0.0.6/crates/tf_tree_core/src/frame.rs +439 -0
  77. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_core/src/layout.rs +21 -115
  78. transform_tree-0.0.6/crates/tf_tree_core/src/lib.rs +108 -0
  79. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_core/src/loom_tests.rs +346 -334
  80. transform_tree-0.0.6/crates/tf_tree_core/src/participant.rs +353 -0
  81. transform_tree-0.0.6/crates/tf_tree_core/src/plan.rs +1977 -0
  82. transform_tree-0.0.6/crates/tf_tree_core/src/sample.rs +522 -0
  83. transform_tree-0.0.6/crates/tf_tree_core/src/sync.rs +28 -0
  84. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_core/src/tests.rs +1038 -736
  85. transform_tree-0.0.6/crates/tf_tree_core/src/topology.rs +583 -0
  86. transform_tree-0.0.6/crates/tf_tree_ingest/Cargo.toml +65 -0
  87. transform_tree-0.0.6/crates/tf_tree_ingest/examples/gen_zstd_conformance.rs +124 -0
  88. transform_tree-0.0.6/crates/tf_tree_ingest/src/cdr.rs +379 -0
  89. transform_tree-0.0.6/crates/tf_tree_ingest/src/decompress.rs +1225 -0
  90. transform_tree-0.0.6/crates/tf_tree_ingest/src/fixture.rs +1292 -0
  91. transform_tree-0.0.6/crates/tf_tree_ingest/src/ingest.rs +1046 -0
  92. transform_tree-0.0.6/crates/tf_tree_ingest/src/lib.rs +315 -0
  93. transform_tree-0.0.6/crates/tf_tree_ingest/src/report.rs +654 -0
  94. transform_tree-0.0.6/crates/tf_tree_ingest/src/source.rs +622 -0
  95. transform_tree-0.0.6/crates/tf_tree_ingest/src/spill.rs +550 -0
  96. transform_tree-0.0.6/crates/tf_tree_ingest/src/tft.rs +33 -0
  97. transform_tree-0.0.6/crates/tf_tree_ingest/testdata/ATTRIBUTION.md +35 -0
  98. transform_tree-0.0.6/crates/tf_tree_ingest/testdata/zstd_conformance.mcap +0 -0
  99. transform_tree-0.0.6/crates/tf_tree_ingest/tests/anomaly_corpus.rs +250 -0
  100. transform_tree-0.0.6/crates/tf_tree_ingest/tests/codec_free.rs +108 -0
  101. transform_tree-0.0.6/crates/tf_tree_ingest/tests/frozen_bag.rs +85 -0
  102. transform_tree-0.0.6/crates/tf_tree_ingest/tests/ingest.rs +2383 -0
  103. transform_tree-0.0.6/crates/tf_tree_ingest/tests/memory.rs +131 -0
  104. transform_tree-0.0.6/crates/tf_tree_ingest/tests/record_ceiling.rs +447 -0
  105. transform_tree-0.0.6/crates/tf_tree_ipc/Cargo.toml +40 -0
  106. transform_tree-0.0.6/crates/tf_tree_ipc/NOTICE +31 -0
  107. transform_tree-0.0.6/crates/tf_tree_ipc/README.md +55 -0
  108. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/bin/ipc_child.rs +12 -47
  109. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/client.rs +131 -72
  110. transform_tree-0.0.6/crates/tf_tree_ipc/src/error.rs +1047 -0
  111. transform_tree-0.0.6/crates/tf_tree_ipc/src/fork.rs +82 -0
  112. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/identity.rs +102 -67
  113. transform_tree-0.0.6/crates/tf_tree_ipc/src/lib.rs +92 -0
  114. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/lockfile.rs +128 -133
  115. transform_tree-0.0.6/crates/tf_tree_ipc/src/ofd.rs +161 -0
  116. transform_tree-0.0.6/crates/tf_tree_ipc/src/open.rs +697 -0
  117. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/procstat.rs +90 -51
  118. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/rendezvous.rs +12 -36
  119. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/runtime_dir.rs +28 -107
  120. transform_tree-0.0.6/crates/tf_tree_ipc/src/server.rs +465 -0
  121. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/src/wire.rs +36 -114
  122. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/tests/multiprocess.rs +106 -169
  123. transform_tree-0.0.6/crates/tf_tree_math/NOTICE +31 -0
  124. transform_tree-0.0.6/crates/tf_tree_math/README.md +75 -0
  125. transform_tree-0.0.6/crates/tf_tree_math/src/dualquat.rs +388 -0
  126. transform_tree-0.0.6/crates/tf_tree_math/src/interp.rs +432 -0
  127. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/src/iso3.rs +23 -100
  128. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/src/lib.rs +5 -12
  129. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/src/quat.rs +10 -49
  130. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/src/twist.rs +23 -92
  131. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/tests/proptests.rs +16 -59
  132. transform_tree-0.0.6/crates/tf_tree_math/tests/slerp_public.rs +464 -0
  133. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_py/Cargo.lock +232 -6
  134. transform_tree-0.0.6/crates/tf_tree_py/Cargo.toml +68 -0
  135. transform_tree-0.0.6/crates/tf_tree_py/src/errors.rs +919 -0
  136. transform_tree-0.0.6/crates/tf_tree_py/src/ingest.rs +114 -0
  137. transform_tree-0.0.6/crates/tf_tree_py/src/lib.rs +169 -0
  138. transform_tree-0.0.6/crates/tf_tree_py/src/offline.rs +317 -0
  139. transform_tree-0.0.6/crates/tf_tree_py/src/tree.rs +1645 -0
  140. transform_tree-0.0.6/pyproject.toml +65 -0
  141. transform_tree-0.0.6/python/tf_tree/__init__.py +112 -0
  142. transform_tree-0.0.6/python/tf_tree/_core.pyi +664 -0
  143. transform_tree-0.0.4/Cargo.toml +0 -231
  144. transform_tree-0.0.4/PKG-INFO +0 -321
  145. transform_tree-0.0.4/README.md +0 -300
  146. transform_tree-0.0.4/crates/tf_tree/Cargo.toml +0 -292
  147. transform_tree-0.0.4/crates/tf_tree/README.md +0 -162
  148. transform_tree-0.0.4/crates/tf_tree/src/cache.rs +0 -617
  149. transform_tree-0.0.4/crates/tf_tree/src/cbor.rs +0 -213
  150. transform_tree-0.0.4/crates/tf_tree/src/frozen.rs +0 -326
  151. transform_tree-0.0.4/crates/tf_tree/src/lib.rs +0 -372
  152. transform_tree-0.0.4/crates/tf_tree/src/open.rs +0 -1719
  153. transform_tree-0.0.4/crates/tf_tree/src/unstable.rs +0 -99
  154. transform_tree-0.0.4/crates/tf_tree/tests/await_frames.rs +0 -203
  155. transform_tree-0.0.4/crates/tf_tree/tests/enumeration.rs +0 -171
  156. transform_tree-0.0.4/crates/tf_tree/tests/feature_gates.rs +0 -171
  157. transform_tree-0.0.4/crates/tf_tree/tests/frozen.rs +0 -629
  158. transform_tree-0.0.4/crates/tf_tree/tests/math_reexports.rs +0 -138
  159. transform_tree-0.0.4/crates/tf_tree/tests/owned_writer.rs +0 -363
  160. transform_tree-0.0.4/crates/tf_tree/tests/plan_cache_identity.rs +0 -353
  161. transform_tree-0.0.4/crates/tf_tree/tests/rendezvous.rs +0 -3037
  162. transform_tree-0.0.4/crates/tf_tree_arena/README.md +0 -87
  163. transform_tree-0.0.4/crates/tf_tree_arena/src/check.rs +0 -312
  164. transform_tree-0.0.4/crates/tf_tree_arena/src/frozen.rs +0 -1017
  165. transform_tree-0.0.4/crates/tf_tree_arena/src/header.rs +0 -415
  166. transform_tree-0.0.4/crates/tf_tree_arena/src/lib.rs +0 -65
  167. transform_tree-0.0.4/crates/tf_tree_arena/src/mapped.rs +0 -870
  168. transform_tree-0.0.4/crates/tf_tree_arena/tests/heap_alignment.rs +0 -139
  169. transform_tree-0.0.4/crates/tf_tree_core/Cargo.toml +0 -69
  170. transform_tree-0.0.4/crates/tf_tree_core/README.md +0 -88
  171. transform_tree-0.0.4/crates/tf_tree_core/src/arena_view.rs +0 -688
  172. transform_tree-0.0.4/crates/tf_tree_core/src/bench_probe.rs +0 -57
  173. transform_tree-0.0.4/crates/tf_tree_core/src/buffer.rs +0 -385
  174. transform_tree-0.0.4/crates/tf_tree_core/src/counters.rs +0 -210
  175. transform_tree-0.0.4/crates/tf_tree_core/src/edge.rs +0 -551
  176. transform_tree-0.0.4/crates/tf_tree_core/src/error.rs +0 -338
  177. transform_tree-0.0.4/crates/tf_tree_core/src/frame.rs +0 -614
  178. transform_tree-0.0.4/crates/tf_tree_core/src/lib.rs +0 -176
  179. transform_tree-0.0.4/crates/tf_tree_core/src/participant.rs +0 -442
  180. transform_tree-0.0.4/crates/tf_tree_core/src/plan.rs +0 -2714
  181. transform_tree-0.0.4/crates/tf_tree_core/src/sample.rs +0 -583
  182. transform_tree-0.0.4/crates/tf_tree_core/src/sync.rs +0 -47
  183. transform_tree-0.0.4/crates/tf_tree_core/src/topology.rs +0 -820
  184. transform_tree-0.0.4/crates/tf_tree_ipc/Cargo.toml +0 -80
  185. transform_tree-0.0.4/crates/tf_tree_ipc/NOTICE +0 -30
  186. transform_tree-0.0.4/crates/tf_tree_ipc/README.md +0 -97
  187. transform_tree-0.0.4/crates/tf_tree_ipc/src/error.rs +0 -586
  188. transform_tree-0.0.4/crates/tf_tree_ipc/src/fork.rs +0 -161
  189. transform_tree-0.0.4/crates/tf_tree_ipc/src/lib.rs +0 -130
  190. transform_tree-0.0.4/crates/tf_tree_ipc/src/ofd.rs +0 -233
  191. transform_tree-0.0.4/crates/tf_tree_ipc/src/open.rs +0 -777
  192. transform_tree-0.0.4/crates/tf_tree_ipc/src/server.rs +0 -635
  193. transform_tree-0.0.4/crates/tf_tree_math/NOTICE +0 -30
  194. transform_tree-0.0.4/crates/tf_tree_math/README.md +0 -131
  195. transform_tree-0.0.4/crates/tf_tree_math/src/dualquat.rs +0 -557
  196. transform_tree-0.0.4/crates/tf_tree_math/src/interp.rs +0 -807
  197. transform_tree-0.0.4/crates/tf_tree_math/tests/slerp_public.rs +0 -654
  198. transform_tree-0.0.4/crates/tf_tree_py/Cargo.toml +0 -99
  199. transform_tree-0.0.4/crates/tf_tree_py/src/errors.rs +0 -1066
  200. transform_tree-0.0.4/crates/tf_tree_py/src/lib.rs +0 -296
  201. transform_tree-0.0.4/crates/tf_tree_py/src/offline.rs +0 -642
  202. transform_tree-0.0.4/crates/tf_tree_py/src/tree.rs +0 -1740
  203. transform_tree-0.0.4/pyproject.toml +0 -91
  204. transform_tree-0.0.4/python/tf_tree/__init__.py +0 -107
  205. transform_tree-0.0.4/python/tf_tree/_core.pyi +0 -544
  206. {transform_tree-0.0.4 → transform_tree-0.0.6}/LICENSE-APACHE +0 -0
  207. {transform_tree-0.0.4 → transform_tree-0.0.6}/LICENSE-MIT +0 -0
  208. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/LICENSE-APACHE +0 -0
  209. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree/LICENSE-MIT +0 -0
  210. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/Cargo.toml +0 -0
  211. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/LICENSE-APACHE +0 -0
  212. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_arena/LICENSE-MIT +0 -0
  213. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_core/LICENSE-APACHE +0 -0
  214. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_core/LICENSE-MIT +0 -0
  215. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/LICENSE-APACHE +0 -0
  216. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_ipc/LICENSE-MIT +0 -0
  217. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/Cargo.toml +0 -0
  218. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/LICENSE-APACHE +0 -0
  219. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/LICENSE-MIT +0 -0
  220. {transform_tree-0.0.4 → transform_tree-0.0.6}/crates/tf_tree_math/src/reference.rs +0 -0
  221. {transform_tree-0.0.4 → transform_tree-0.0.6}/python/tf_tree/py.typed +0 -0
@@ -0,0 +1,120 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = [
4
+ "crates/tf_tree_math",
5
+ "crates/tf_tree_arena",
6
+ "crates/tf_tree_core",
7
+ "crates/tf_tree_ipc",
8
+ "crates/tf_tree",
9
+ "crates/tf_tree_bench",
10
+ "crates/tf_tree_cli",
11
+ "crates/tf_tree_bridge",
12
+ "crates/tf_tree_ingest",
13
+ "crates/tf_tree_c",
14
+ "xtask",
15
+ ]
16
+ # `tf_tree_tf2_sys` (ROS 2) and `tf_tree_py` (libpython) are excluded so
17
+ # `cargo build --workspace` works on every host.
18
+ exclude = [
19
+ "crates/tf_tree_py","crates/tf_tree_tf2_sys"]
20
+
21
+ [workspace.package]
22
+ # 0.0.x: every release may break every other (`SUPPORT.md`).
23
+ version = "0.0.6"
24
+ edition = "2021"
25
+ # 1.87 is forced by `ruzstd 0.9.0` (`tf_tree_ingest`). The `msrv` job reads this
26
+ # field; policy in `SUPPORT.md`.
27
+ rust-version = "1.87"
28
+ # Publishable crates symlink `LICENSE-*` and `NOTICE`; publish from Linux.
29
+ license = "MIT OR Apache-2.0"
30
+ repository = "https://github.com/NoeFontana/tf_tree"
31
+ homepage = "https://github.com/NoeFontana/tf_tree"
32
+ authors = ["Noé Fontana <noe.fontana.pro@gmail.com>"]
33
+ keywords = ["robotics", "tf", "transform", "se3", "ros"]
34
+ categories = ["science::robotics", "no-std"]
35
+
36
+ [workspace.lints.clippy]
37
+ unwrap_used = "deny"
38
+ expect_used = "deny"
39
+ panic = "deny"
40
+ todo = "deny"
41
+ unimplemented = "deny"
42
+ dbg_macro = "deny"
43
+ print_stdout = "warn"
44
+ print_stderr = "warn"
45
+ needless_pass_by_value = "warn"
46
+ redundant_clone = "warn"
47
+ # 0007 rule 3: every `unsafe` block has a `// SAFETY:` comment directly above it.
48
+ # `tf_tree_py` and `tf_tree_tf2_sys` repeat it.
49
+ undocumented_unsafe_blocks = "deny"
50
+
51
+ [workspace.lints.rust]
52
+ missing_docs = "warn"
53
+ unreachable_pub = "warn"
54
+ unused_must_use = "deny"
55
+ # Registers `--cfg loom` so `-D warnings` accepts it.
56
+ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] }
57
+
58
+ [workspace.dependencies]
59
+ # Intra-workspace crates.
60
+ tf_tree_math = { path = "crates/tf_tree_math", version = "0.0.6" }
61
+ tf_tree_arena = { path = "crates/tf_tree_arena", version = "0.0.6" }
62
+ # `shm` (`docs/PHASE2.md` §2): `linux_raw` backend, no C build; used only by `tf_tree_ipc`.
63
+ libc = "0.2"
64
+ rustix = { version = "1.1", default-features = false, features = ["mm", "fs", "std"] }
65
+ # `default-features = false` is load-bearing: without it `--no-default-features`
66
+ # downstream still enables `counters` (`docs/PHASE5.md` §5.5).
67
+ tf_tree_core = { path = "crates/tf_tree_core", version = "0.0.6", default-features = false }
68
+ tf_tree_ipc = { path = "crates/tf_tree_ipc", version = "0.0.6" }
69
+ tf_tree = { path = "crates/tf_tree", version = "0.0.6", default-features = false }
70
+ # publish = false crates; `default-features = false` as above.
71
+ tf_tree_bench = { path = "crates/tf_tree_bench", default-features = false }
72
+ tf_tree_bridge = { path = "crates/tf_tree_bridge" }
73
+ # `docs/PHASE5.md` §3. The CLI decides whether the engine counts.
74
+ tf_tree_ingest = { path = "crates/tf_tree_ingest", default-features = false }
75
+ # no_std numeric + POD casting (the entire core dependency budget, plus blake3).
76
+ libm = "0.2"
77
+ bytemuck = { version = "1", features = ["derive"] }
78
+ blake3 = { version = "1", default-features = false }
79
+ # std-side facade / tooling only.
80
+ thiserror = "2"
81
+ anyhow = "1"
82
+ clap = { version = "4", features = ["derive"] }
83
+ # dev / bench only.
84
+ proptest = "1"
85
+ loom = "0.7"
86
+ criterion = { version = "0.5", features = ["html_reports"] }
87
+ serde_json = "1"
88
+
89
+ [profile.release]
90
+ lto = "thin"
91
+ codegen-units = 1
92
+ debug = "line-tables-only"
93
+ strip = "debuginfo"
94
+
95
+ [profile.dev]
96
+ opt-level = 0
97
+
98
+ [profile.bench]
99
+ lto = "thin"
100
+ codegen-units = 1
101
+
102
+ # Release codegen with debuginfo for profilers.
103
+ [profile.profiling]
104
+ inherits = "release"
105
+ debug = "full"
106
+ strip = "none"
107
+
108
+ # An embedder's `cargo build --release` (`docs/PHASE5.md` §9.2, `docs/API.md` §2.3):
109
+ # every default spelled out; `crates/tf_tree_bench/src/embed.rs` tests these values.
110
+ [profile.embedder]
111
+ inherits = "release"
112
+ opt-level = 3
113
+ lto = false
114
+ codegen-units = 16
115
+ debug = false
116
+ strip = "none"
117
+ debug-assertions = false
118
+ overflow-checks = false
119
+ incremental = false
120
+ panic = "unwind"
@@ -19,7 +19,8 @@ carries no third-party attributions of its own: `tf_tree` vendors no source and
19
19
  statically links no code it did not write. Its Rust dependencies are ordinary
20
20
  crates.io dependencies, each under its own licence, resolved at build time and
21
21
  enumerated in `Cargo.lock`; the set is constrained by `deny.toml` and checked by
22
- `cargo deny check`, which `just lint` runs.
22
+ `cargo deny check` — the recipe `just audit`, run in CI as its own step of the
23
+ job named `lint`.
23
24
 
24
25
  `tf_tree` is not affiliated with, endorsed by, or derived from the Open Source
25
26
  Robotics Foundation, ROS, or the `tf` / `tf2` packages. It is an independent
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: transform_tree
3
+ Version: 0.0.6
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Programming Language :: Python :: 3.10
6
+ Classifier: Programming Language :: Python :: 3.13
7
+ Classifier: Programming Language :: Python :: 3.14
8
+ Classifier: Programming Language :: Rust
9
+ Classifier: Topic :: Scientific/Engineering
10
+ Requires-Dist: numpy>=2.2.6
11
+ License-File: LICENSE-APACHE
12
+ License-File: LICENSE-MIT
13
+ License-File: NOTICE
14
+ Summary: A transform tree engine — faster and more scalable than ROS tf2
15
+ Home-Page: https://github.com/NoeFontana/tf_tree
16
+ Author-email: Noé Fontana <noe.fontana.pro@gmail.com>
17
+ License-Expression: MIT OR Apache-2.0
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
20
+
21
+ # tf_tree
22
+
23
+ [![crates.io](https://img.shields.io/crates/v/tf_tree.svg?logo=rust)](https://crates.io/crates/tf_tree)
24
+ [![docs.rs](https://img.shields.io/docsrs/tf_tree?logo=docsdotrs)](https://docs.rs/tf_tree)
25
+ [![PyPI](https://img.shields.io/pypi/v/transform_tree.svg?logo=pypi&logoColor=white)](https://pypi.org/project/transform_tree/)
26
+ [![CI](https://github.com/NoeFontana/tf_tree/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/NoeFontana/tf_tree/actions/workflows/ci.yml)
27
+ [![Licence](https://img.shields.io/badge/licence-MIT%20OR%20Apache--2.0-blue.svg)](#licence)
28
+
29
+ A transform tree: it stores time-stamped poses between named frames and answers
30
+ "where was frame A relative to frame B at time t?". Use it in a control loop,
31
+ across processes on one host, or in a dataloader with no robot attached.
32
+
33
+ It is faster than ROS `tf2` (below) but is not `tf2`: no ROS dependency and no
34
+ drop-in `tf2_ros::Buffer`, only a one-way `/tf` ingest bridge
35
+ ([`0008`](./docs/decisions/0008-the-name-tf-tree.md)). Linux first: shared memory
36
+ and frozen `.tft` files are Linux-only (`shm` feature); the in-process engine is
37
+ portable.
38
+
39
+ **`0.0.x` promises nothing between releases.** Pin exactly ([`CHANGELOG.md`](./CHANGELOG.md)).
40
+
41
+ ## Install
42
+
43
+ - Rust: `cargo add tf_tree`
44
+ - Python: `pip install transform_tree`, then `import tf_tree`
45
+ - CLI (`tf_tree`, alias `tft`): a binary from the [latest release](https://github.com/NoeFontana/tf_tree/releases/latest), or `cargo install --path crates/tf_tree_cli --features shm`
46
+ - C ABI, C++ header, ROS 2 bridge: `just c-abi-check`, `just cpp-check`, `just ros-build`
47
+
48
+ ## Quickstart
49
+
50
+ Python, from scratch (`just quickstart` runs this). Stamps are integer nanoseconds; a pose is `[qw, qx, qy, qz, x, y, z]`.
51
+
52
+ ```python
53
+ import tf_tree
54
+
55
+ tree = tf_tree.build([("map", "base"), ("base", "cam")])
56
+ tf_tree.push(tree, "base", "map", 1_000, [1, 0, 0, 0, 1.0, 2.0, 3.0])
57
+ tf_tree.push(tree, "base", "map", 2_000, [1, 0, 0, 0, 3.0, 4.0, 5.0])
58
+
59
+ plan = tree.plan("map", "base") # compile the route once, reuse it
60
+ print(plan.at(1_500)[:3, 3]) # -> [2. 3. 4.]
61
+ ```
62
+
63
+ Rust:
64
+
65
+ ```rust
66
+ use tf_tree::{Capacity, EdgeCfg, Iso3, Quat, Stamp, TreeBuilder, Vec3};
67
+
68
+ let tree = TreeBuilder::new()
69
+ .static_edge("base_link", "lidar_top", &Iso3::IDENTITY) // (parent, child)
70
+ .dynamic_edge("odom", "base_link", EdgeCfg::new(Capacity::history(100.0, 10.0)))
71
+ .build()
72
+ .expect("layout");
73
+ let odom = tree.frame("odom").expect("declared");
74
+ let base_link = tree.frame("base_link").expect("declared");
75
+ let lidar_top = tree.frame("lidar_top").expect("declared");
76
+
77
+ let w = tree.claim(base_link, odom).expect("unclaimed"); // (child, parent)
78
+ let at_x = |x| Iso3::new(Quat::IDENTITY, Vec3::new(x, 0.0, 0.0));
79
+ w.push(1_000_000_000, &at_x(0.0)).expect("monotonic");
80
+ w.push(1_010_000_000, &at_x(1.0)).expect("monotonic");
81
+
82
+ let plan = tree.plan(odom, lidar_top).expect("connected");
83
+ let g = tree.guard();
84
+ let t: Stamp = Stamp::from_nanos(1_005_000_000);
85
+ let pose = plan.at(&g, t).expect("in range");
86
+ assert!((pose.t.x - 0.5).abs() < 1e-12);
87
+ ```
88
+
89
+ ## From a recorded bag
90
+
91
+ MCAP only (for `.db3`, run `ros2 bag convert` first):
92
+
93
+ ```sh
94
+ tft doctor --from-bag drive.mcap # what is wrong with this /tf traffic
95
+ tft freeze --from-bag drive.mcap -o drive.tft # index it
96
+ ```
97
+ ```python
98
+ tree = tf_tree.open_file("drive.tft") # an mmap; open per worker, after fork/spawn
99
+ poses = tree.plan("base_link", "lidar_top").at(stamps_ns, layout="quat_twist") # (N, 13)
100
+ ```
101
+
102
+ ## Against `tf2`
103
+
104
+ In-process, one thread, same interpolation, `tf2` called natively; 4-core EPYC-Milan,
105
+ not core-pinned. Both agree to 7e-15 on 50,000 recorded queries.
106
+
107
+ | | `tf_tree` | `tf2` | |
108
+ |---|---|---|---|
109
+ | Lookup, depth 3 | 94 ns | 253 ns | 2.7× |
110
+ | Lookup, depth 15 | 1193 ns | 7337 ns | 6.2× |
111
+ | Publish | 9.4 ns | 114 ns | 12.1× |
112
+
113
+ Without LTO the lookup gap is 1.8×; through the C ABI, 1.5×. Method:
114
+ [`tf2.md`](./docs/benchmarks/tf2.md); sources: [`EVIDENCE.md`](./docs/benchmarks/EVIDENCE.md).
115
+
116
+ ## Not for
117
+
118
+ | Need | Why | See |
119
+ |---|---|---|
120
+ | Drop-in `tf2_ros::Buffer` | gated, not scheduled | [`PHASE7.md`](./docs/PHASE7.md) |
121
+ | Covariance, multi-parent frames, loop closure | cut | [`0009`](./docs/decisions/0009-descoping-phase-6.md) |
122
+ | Transforms across hosts | not started | [`PROJECT.md`](./docs/PROJECT.md) §4 |
123
+ | Shared memory or `.tft` off Linux | not implemented | [`SUPPORT.md`](./SUPPORT.md) |
124
+ | A viewer, point-cloud deskewing | deliberately absent | [`PHASE5.md`](./docs/PHASE5.md) §8 |
125
+
126
+ ## Status
127
+
128
+ | Phase | | Status |
129
+ |---|---|---|
130
+ | 1 | Engine: arena, seqlock buffers, plans, SE(3) math | Implemented |
131
+ | 2 | Shared memory across processes | Implemented, with gaps |
132
+ | 3 | Python bindings | Implemented |
133
+ | 4 | C ABI, C++ wrapper, ROS 2 ingest, derivatives | Implemented, with gaps |
134
+ | 5 | Frozen `.tft`, bag ingestion, diagnostics, `tf_tree top` | Mostly implemented |
135
+ | 6–8 | Continuous time, `tf2` shim, multi-host | Not started |
136
+
137
+ The `§0.0` tables in [`PHASE2`](./docs/PHASE2.md#00-implementation-status),
138
+ [`PHASE4`](./docs/PHASE4.md#00-implementation-status) and
139
+ [`PHASE5`](./docs/PHASE5.md#00-implementation-status) win over this one.
140
+
141
+ ## Shared memory is not a sandbox
142
+
143
+ Processes sharing an arena trust each other: a read-write participant can corrupt
144
+ it ([`PHASE2.md`](./docs/PHASE2.md) §3.10; [`SECURITY.md`](./SECURITY.md)). A
145
+ read-only participant (the default for readers) cannot, and a crashed or hung one
146
+ cannot wedge the others.
147
+
148
+ `fork()` is the sharp edge: a child's inherited handles report `ChildDetached`.
149
+ Python's `multiprocessing` forks by default on Linux, so open the arena inside the
150
+ worker or use `spawn`. A frozen `.tft` survives a fork.
151
+
152
+ ## Layout
153
+
154
+ `crates/`: `tf_tree_math`, `tf_tree_arena`, `tf_tree_core`, `tf_tree` (facade),
155
+ `tf_tree_ipc`, `tf_tree_c`, `tf_tree_bridge`, `tf_tree_ingest`, `tf_tree_py`,
156
+ `tf_tree_bench`, `tf_tree_tf2_sys`, `tf_tree_cli`. `ros/` holds the ROS 2
157
+ packages. `tf_tree_py`, `tf_tree_tf2_sys` and `ros/` sit outside the cargo
158
+ workspace and have their own `just` recipes. Five crates publish: `tf_tree`,
159
+ `tf_tree_core`, `tf_tree_math`, `tf_tree_arena`, `tf_tree_ipc`. MSRV **1.87**.
160
+
161
+ Docs: [`docs/README.md`](./docs/README.md) ·
162
+ [`CONTRIBUTING.md`](./CONTRIBUTING.md) · [`SUPPORT.md`](./SUPPORT.md) ·
163
+ [`SECURITY.md`](./SECURITY.md) · [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md)
164
+
165
+ ## Licence
166
+
167
+ Dual [MIT](./LICENSE-MIT) / [Apache-2.0](./LICENSE-APACHE), at your option. See
168
+ [`NOTICE`](./NOTICE).
169
+
@@ -0,0 +1,148 @@
1
+ # tf_tree
2
+
3
+ [![crates.io](https://img.shields.io/crates/v/tf_tree.svg?logo=rust)](https://crates.io/crates/tf_tree)
4
+ [![docs.rs](https://img.shields.io/docsrs/tf_tree?logo=docsdotrs)](https://docs.rs/tf_tree)
5
+ [![PyPI](https://img.shields.io/pypi/v/transform_tree.svg?logo=pypi&logoColor=white)](https://pypi.org/project/transform_tree/)
6
+ [![CI](https://github.com/NoeFontana/tf_tree/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/NoeFontana/tf_tree/actions/workflows/ci.yml)
7
+ [![Licence](https://img.shields.io/badge/licence-MIT%20OR%20Apache--2.0-blue.svg)](#licence)
8
+
9
+ A transform tree: it stores time-stamped poses between named frames and answers
10
+ "where was frame A relative to frame B at time t?". Use it in a control loop,
11
+ across processes on one host, or in a dataloader with no robot attached.
12
+
13
+ It is faster than ROS `tf2` (below) but is not `tf2`: no ROS dependency and no
14
+ drop-in `tf2_ros::Buffer`, only a one-way `/tf` ingest bridge
15
+ ([`0008`](./docs/decisions/0008-the-name-tf-tree.md)). Linux first: shared memory
16
+ and frozen `.tft` files are Linux-only (`shm` feature); the in-process engine is
17
+ portable.
18
+
19
+ **`0.0.x` promises nothing between releases.** Pin exactly ([`CHANGELOG.md`](./CHANGELOG.md)).
20
+
21
+ ## Install
22
+
23
+ - Rust: `cargo add tf_tree`
24
+ - Python: `pip install transform_tree`, then `import tf_tree`
25
+ - CLI (`tf_tree`, alias `tft`): a binary from the [latest release](https://github.com/NoeFontana/tf_tree/releases/latest), or `cargo install --path crates/tf_tree_cli --features shm`
26
+ - C ABI, C++ header, ROS 2 bridge: `just c-abi-check`, `just cpp-check`, `just ros-build`
27
+
28
+ ## Quickstart
29
+
30
+ Python, from scratch (`just quickstart` runs this). Stamps are integer nanoseconds; a pose is `[qw, qx, qy, qz, x, y, z]`.
31
+
32
+ ```python
33
+ import tf_tree
34
+
35
+ tree = tf_tree.build([("map", "base"), ("base", "cam")])
36
+ tf_tree.push(tree, "base", "map", 1_000, [1, 0, 0, 0, 1.0, 2.0, 3.0])
37
+ tf_tree.push(tree, "base", "map", 2_000, [1, 0, 0, 0, 3.0, 4.0, 5.0])
38
+
39
+ plan = tree.plan("map", "base") # compile the route once, reuse it
40
+ print(plan.at(1_500)[:3, 3]) # -> [2. 3. 4.]
41
+ ```
42
+
43
+ Rust:
44
+
45
+ ```rust
46
+ use tf_tree::{Capacity, EdgeCfg, Iso3, Quat, Stamp, TreeBuilder, Vec3};
47
+
48
+ let tree = TreeBuilder::new()
49
+ .static_edge("base_link", "lidar_top", &Iso3::IDENTITY) // (parent, child)
50
+ .dynamic_edge("odom", "base_link", EdgeCfg::new(Capacity::history(100.0, 10.0)))
51
+ .build()
52
+ .expect("layout");
53
+ let odom = tree.frame("odom").expect("declared");
54
+ let base_link = tree.frame("base_link").expect("declared");
55
+ let lidar_top = tree.frame("lidar_top").expect("declared");
56
+
57
+ let w = tree.claim(base_link, odom).expect("unclaimed"); // (child, parent)
58
+ let at_x = |x| Iso3::new(Quat::IDENTITY, Vec3::new(x, 0.0, 0.0));
59
+ w.push(1_000_000_000, &at_x(0.0)).expect("monotonic");
60
+ w.push(1_010_000_000, &at_x(1.0)).expect("monotonic");
61
+
62
+ let plan = tree.plan(odom, lidar_top).expect("connected");
63
+ let g = tree.guard();
64
+ let t: Stamp = Stamp::from_nanos(1_005_000_000);
65
+ let pose = plan.at(&g, t).expect("in range");
66
+ assert!((pose.t.x - 0.5).abs() < 1e-12);
67
+ ```
68
+
69
+ ## From a recorded bag
70
+
71
+ MCAP only (for `.db3`, run `ros2 bag convert` first):
72
+
73
+ ```sh
74
+ tft doctor --from-bag drive.mcap # what is wrong with this /tf traffic
75
+ tft freeze --from-bag drive.mcap -o drive.tft # index it
76
+ ```
77
+ ```python
78
+ tree = tf_tree.open_file("drive.tft") # an mmap; open per worker, after fork/spawn
79
+ poses = tree.plan("base_link", "lidar_top").at(stamps_ns, layout="quat_twist") # (N, 13)
80
+ ```
81
+
82
+ ## Against `tf2`
83
+
84
+ In-process, one thread, same interpolation, `tf2` called natively; 4-core EPYC-Milan,
85
+ not core-pinned. Both agree to 7e-15 on 50,000 recorded queries.
86
+
87
+ | | `tf_tree` | `tf2` | |
88
+ |---|---|---|---|
89
+ | Lookup, depth 3 | 94 ns | 253 ns | 2.7× |
90
+ | Lookup, depth 15 | 1193 ns | 7337 ns | 6.2× |
91
+ | Publish | 9.4 ns | 114 ns | 12.1× |
92
+
93
+ Without LTO the lookup gap is 1.8×; through the C ABI, 1.5×. Method:
94
+ [`tf2.md`](./docs/benchmarks/tf2.md); sources: [`EVIDENCE.md`](./docs/benchmarks/EVIDENCE.md).
95
+
96
+ ## Not for
97
+
98
+ | Need | Why | See |
99
+ |---|---|---|
100
+ | Drop-in `tf2_ros::Buffer` | gated, not scheduled | [`PHASE7.md`](./docs/PHASE7.md) |
101
+ | Covariance, multi-parent frames, loop closure | cut | [`0009`](./docs/decisions/0009-descoping-phase-6.md) |
102
+ | Transforms across hosts | not started | [`PROJECT.md`](./docs/PROJECT.md) §4 |
103
+ | Shared memory or `.tft` off Linux | not implemented | [`SUPPORT.md`](./SUPPORT.md) |
104
+ | A viewer, point-cloud deskewing | deliberately absent | [`PHASE5.md`](./docs/PHASE5.md) §8 |
105
+
106
+ ## Status
107
+
108
+ | Phase | | Status |
109
+ |---|---|---|
110
+ | 1 | Engine: arena, seqlock buffers, plans, SE(3) math | Implemented |
111
+ | 2 | Shared memory across processes | Implemented, with gaps |
112
+ | 3 | Python bindings | Implemented |
113
+ | 4 | C ABI, C++ wrapper, ROS 2 ingest, derivatives | Implemented, with gaps |
114
+ | 5 | Frozen `.tft`, bag ingestion, diagnostics, `tf_tree top` | Mostly implemented |
115
+ | 6–8 | Continuous time, `tf2` shim, multi-host | Not started |
116
+
117
+ The `§0.0` tables in [`PHASE2`](./docs/PHASE2.md#00-implementation-status),
118
+ [`PHASE4`](./docs/PHASE4.md#00-implementation-status) and
119
+ [`PHASE5`](./docs/PHASE5.md#00-implementation-status) win over this one.
120
+
121
+ ## Shared memory is not a sandbox
122
+
123
+ Processes sharing an arena trust each other: a read-write participant can corrupt
124
+ it ([`PHASE2.md`](./docs/PHASE2.md) §3.10; [`SECURITY.md`](./SECURITY.md)). A
125
+ read-only participant (the default for readers) cannot, and a crashed or hung one
126
+ cannot wedge the others.
127
+
128
+ `fork()` is the sharp edge: a child's inherited handles report `ChildDetached`.
129
+ Python's `multiprocessing` forks by default on Linux, so open the arena inside the
130
+ worker or use `spawn`. A frozen `.tft` survives a fork.
131
+
132
+ ## Layout
133
+
134
+ `crates/`: `tf_tree_math`, `tf_tree_arena`, `tf_tree_core`, `tf_tree` (facade),
135
+ `tf_tree_ipc`, `tf_tree_c`, `tf_tree_bridge`, `tf_tree_ingest`, `tf_tree_py`,
136
+ `tf_tree_bench`, `tf_tree_tf2_sys`, `tf_tree_cli`. `ros/` holds the ROS 2
137
+ packages. `tf_tree_py`, `tf_tree_tf2_sys` and `ros/` sit outside the cargo
138
+ workspace and have their own `just` recipes. Five crates publish: `tf_tree`,
139
+ `tf_tree_core`, `tf_tree_math`, `tf_tree_arena`, `tf_tree_ipc`. MSRV **1.87**.
140
+
141
+ Docs: [`docs/README.md`](./docs/README.md) ·
142
+ [`CONTRIBUTING.md`](./CONTRIBUTING.md) · [`SUPPORT.md`](./SUPPORT.md) ·
143
+ [`SECURITY.md`](./SECURITY.md) · [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md)
144
+
145
+ ## Licence
146
+
147
+ Dual [MIT](./LICENSE-MIT) / [Apache-2.0](./LICENSE-APACHE), at your option. See
148
+ [`NOTICE`](./NOTICE).
@@ -0,0 +1,74 @@
1
+ [package]
2
+ name = "tf_tree"
3
+ description = "std facade for the tf_tree transform engine: ergonomic builder, lookups, and Display errors."
4
+ # Per-crate crates.io front page (see `tf_tree_math`'s manifest).
5
+ readme = "README.md"
6
+ version.workspace = true
7
+ edition.workspace = true
8
+ rust-version.workspace = true
9
+ license.workspace = true
10
+ repository.workspace = true
11
+ homepage.workspace = true
12
+ authors.workspace = true
13
+ keywords.workspace = true
14
+ # Drops the inherited `no-std`: this is the `std` facade.
15
+ categories = ["science::robotics"]
16
+
17
+ [lints]
18
+ workspace = true
19
+
20
+ [package.metadata.docs.rs]
21
+ all-features = true
22
+ rustdoc-args = ["--cfg", "docsrs"]
23
+
24
+ [features]
25
+ default = ["counters"]
26
+ # Passthrough to `tf_tree_core` (`docs/PHASE5.md` §5.5).
27
+ counters = ["tf_tree_core/counters"]
28
+ pure-hash = ["tf_tree_core/pure-hash"]
29
+ # `docs/PHASE2.md` §11.3's fault injection; arms `crate::CRASH_SITES` too.
30
+ crash-points = ["tf_tree_core/crash-points"]
31
+ # `TreeBuilder::build_shared` / `Tree::attach_shared`. Linux-only.
32
+ shm = ["tf_tree_arena/shm", "dep:tf_tree_ipc"]
33
+ # `docs/API.md` §6's Rust mirror of `tf_tree_unstable.h` (`docs/PHASE4.md` §3.1).
34
+ # Enabling is the waiver: nothing behind it is covered by semver.
35
+ #
36
+ # Enabling consumers, checked by `just stable-tier-check` against this comment and
37
+ # `docs/API.md` §6 row 4: `tf_tree_cli`, `tf_tree_c`, `tf_tree_bench`, `tf_tree_py`.
38
+ unstable = []
39
+ # `#[doc(hidden)]` test seams: `CLAIM_WINDOW_HOOK` (`docs/decisions/0005` §5) and
40
+ # `reclamation_verdict_for_test` (`docs/decisions/0028` step 2). Run by `just shm-rendezvous`.
41
+ test-hooks = []
42
+
43
+ [dependencies]
44
+ tf_tree_core.workspace = true
45
+ tf_tree_math.workspace = true
46
+ tf_tree_arena.workspace = true
47
+ thiserror.workspace = true
48
+
49
+ # The rendezvous (`docs/decisions/0005`); gated on `shm` and Linux.
50
+ [target.'cfg(target_os = "linux")'.dependencies]
51
+ tf_tree_ipc = { workspace = true, optional = true }
52
+
53
+ [dev-dependencies]
54
+ # No self dev-dependency: `cargo package` strips it. Tests gate `arena_view` call
55
+ # sites with `#[cfg(feature = "unstable")]` (`tests/feature_gates.rs`).
56
+ tf_tree_ipc = { workspace = true }
57
+ proptest.workspace = true
58
+
59
+ # `tests/rendezvous.rs`'s second process. Published, so the name is prefixed;
60
+ # `cargo install --features shm` writes it to `~/.cargo/bin` (`README.md`). It
61
+ # stays here because `env!("CARGO_BIN_EXE_…")` needs this package's executable.
62
+ [[bin]]
63
+ name = "tf_tree_rendezvous_child"
64
+ path = "src/bin/rendezvous_child.rs"
65
+ required-features = ["shm"]
66
+
67
+ [[test]]
68
+ name = "rendezvous"
69
+ required-features = ["shm"]
70
+
71
+ # `docs/PHASE5.md` §2; Linux-only, behind `shm`.
72
+ [[test]]
73
+ name = "frozen"
74
+ required-features = ["shm"]
@@ -19,7 +19,8 @@ carries no third-party attributions of its own: `tf_tree` vendors no source and
19
19
  statically links no code it did not write. Its Rust dependencies are ordinary
20
20
  crates.io dependencies, each under its own licence, resolved at build time and
21
21
  enumerated in `Cargo.lock`; the set is constrained by `deny.toml` and checked by
22
- `cargo deny check`, which `just lint` runs.
22
+ `cargo deny check` — the recipe `just audit`, run in CI as its own step of the
23
+ job named `lint`.
23
24
 
24
25
  `tf_tree` is not affiliated with, endorsed by, or derived from the Open Source
25
26
  Robotics Foundation, ROS, or the `tf` / `tf2` packages. It is an independent
@@ -0,0 +1,104 @@
1
+ # tf_tree
2
+
3
+ [![crates.io](https://img.shields.io/crates/v/tf_tree.svg?logo=rust)](https://crates.io/crates/tf_tree)
4
+ [![docs.rs](https://img.shields.io/docsrs/tf_tree?logo=docsdotrs)](https://docs.rs/tf_tree)
5
+ [![Licence](https://img.shields.io/badge/licence-MIT%20OR%20Apache--2.0-blue.svg)](#licence)
6
+
7
+ A transform tree engine: store time-stamped rigid-body transforms between named
8
+ coordinate frames and answer *"where was frame A relative to frame B at time
9
+ t?"* — from a control loop, from many processes at once, or offline.
10
+
11
+ **This is the crate to depend on**: the `std` facade over the `no_std` engine
12
+ ([`tf_tree_core`](https://crates.io/crates/tf_tree_core)). **It is not `tf2` and
13
+ not affiliated with ROS**; there is no drop-in `tf2_ros::Buffer` shim.
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ cargo add tf_tree
19
+ ```
20
+
21
+ Shared arenas, the frozen `.tft` reader and `tf_tree::open()` are behind the
22
+ default-off, **Linux-only** `shm` feature: `cargo add tf_tree --features shm`.
23
+ Python: `pip install transform_tree`.
24
+
25
+ ## In full
26
+
27
+ ```rust
28
+ use tf_tree::{Capacity, EdgeCfg, Iso3, Quat, Stamp, TreeBuilder, Vec3};
29
+
30
+ // Topology is declared up front; nothing allocates after `build()`.
31
+ let tree = TreeBuilder::new()
32
+ .static_edge("base_link", "lidar_top", &Iso3::IDENTITY)
33
+ .dynamic_edge("odom", "base_link", EdgeCfg::new(Capacity::history(100.0, 10.0)))
34
+ .build()
35
+ .expect("layout");
36
+
37
+ let odom = tree.frame("odom").expect("declared");
38
+ let base_link = tree.frame("base_link").expect("declared");
39
+ let lidar_top = tree.frame("lidar_top").expect("declared");
40
+
41
+ // One writer per edge, enforced by the claim table.
42
+ let w = tree.claim(base_link, odom).expect("unclaimed"); // (child, parent)
43
+ let at_x = |x| Iso3::new(Quat::IDENTITY, Vec3::new(x, 0.0, 0.0));
44
+ w.push(1_000_000_000, &at_x(0.0)).expect("monotonic"); // integer nanoseconds
45
+ w.push(1_010_000_000, &at_x(1.0)).expect("monotonic");
46
+
47
+ // Compile the route once, evaluate it many times.
48
+ let plan = tree.plan(odom, lidar_top).expect("connected");
49
+ let g = tree.guard();
50
+
51
+ // The annotation pins the default `SystemDomain`.
52
+ let t: Stamp = Stamp::from_nanos(1_005_000_000);
53
+ match plan.at(&g, t) {
54
+ Ok(pose) => println!("x = {}", pose.t.x), // -> x = 0.5
55
+ Err(e) => println!("{}", tree.describe(e)),
56
+ }
57
+
58
+ let late: Stamp = Stamp::from_nanos(3_000_000_000);
59
+ match plan.at(&g, late) {
60
+ Ok(_) => unreachable!(),
61
+ // -> lookup on odom->base_link (edge#2) would extrapolate:
62
+ // requested 3000000000 ns, history [1000000000, 1010000000] ns
63
+ Err(e) => println!("{}", tree.describe(e)),
64
+ }
65
+ ```
66
+
67
+ **Stamps are integer nanoseconds and carry a domain in the type**; **`plan()` is
68
+ the object you keep** (`lookup()` by name caches one); **errors are `Copy`
69
+ identifiers naming the offending edge**, also `Display` and `std::error::Error`.
70
+
71
+ ## Features
72
+
73
+ | Feature | Default | What it does |
74
+ |---|---|---|
75
+ | `counters` | **on** | Diagnostic counters; off does not fork the layout hash |
76
+ | `shm` | off | `TreeBuilder::build_shared`, `Tree::attach_shared`, `tf_tree::open()`, the frozen `.tft` reader. **Linux only** |
77
+ | `unstable` | off | Arena-shaped introspection (`tf_tree::unstable`, `Tree::arena_view`); **enabling it is the waiver** of semver |
78
+ | `test-hooks` | off | An injection point inside `Tree::claim` for the repository's tests. Not for shipped builds |
79
+
80
+ ## Shared memory is not a sandbox
81
+
82
+ Processes sharing an arena are **mutually trusting, same-user, cooperating
83
+ processes**; a read-write participant can corrupt any part of the arena
84
+ ([`SECURITY.md`](https://github.com/NoeFontana/tf_tree/blob/main/SECURITY.md)).
85
+ `fork()` is the sharp edge: a shared arena is mapped `MADV_DONTFORK`, so a child's
86
+ handles report `ChildDetached`; open inside the worker. A frozen `.tft` is
87
+ inherited intact.
88
+
89
+ ## Version and docs
90
+
91
+ **`0.0.x` promises nothing**: pin exactly
92
+ ([`CHANGELOG.md`](https://github.com/NoeFontana/tf_tree/blob/main/CHANGELOG.md)).
93
+ MSRV is **1.87**
94
+ ([`SUPPORT.md`](https://github.com/NoeFontana/tf_tree/blob/main/SUPPORT.md)).
95
+
96
+ The CLI, C ABI and C++ wrapper, MCAP ingest, ROS 2 bridge and Python bindings are
97
+ built from source; `cargo install tf_tree` installs no command. See the repository
98
+ [`README.md`](https://github.com/NoeFontana/tf_tree/blob/main/README.md),
99
+ [`docs/PROJECT.md`](https://github.com/NoeFontana/tf_tree/blob/main/docs/PROJECT.md)
100
+ and [`docs/API.md`](https://github.com/NoeFontana/tf_tree/blob/main/docs/API.md).
101
+
102
+ ## Licence
103
+
104
+ Dual [MIT](LICENSE-MIT) / [Apache-2.0](LICENSE-APACHE), at your option; see [`NOTICE`](NOTICE).