#!/usr/bin/env python3 """ Generates the permutations of view coordinate name definitions. This will modify the different archetype extensions to include the appropriate constants. """ # TODO(#2388): This script can potentially go away or be significantly reduced. from __future__ import annotations import argparse import itertools import os from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Iterable BEGIN_MARKER = "" END_MARKER = "" SCRIPT_PATH = os.path.relpath(__file__, os.getcwd()).replace("\\", "/") ################################################################################ @dataclass class ViewCoordinates: name: str x: str y: str z: str def is_left_handed(coords: ViewCoordinates) -> bool: def rfd(dir: str) -> tuple[int, int, int]: if dir == "Right": return (1, 0, 0) elif dir == "Left": return (-1, 0, 0) elif dir == "Up": return (0, -1, 0) elif dir == "Down": return (0, 1, 0) elif dir == "Back": return (0, 0, -1) elif dir == "Forward": return (0, 0, 1) else: raise RuntimeWarning(f"Unknown direction: {dir}") m00, m01, m02 = rfd(coords.x) m10, m11, m12 = rfd(coords.y) m20, m21, m22 = rfd(coords.z) deterimnant = ( m00 * m11 * m22 + m01 * m12 * m20 + m02 * m10 * m21 - m02 * m11 * m20 - m01 * m10 * m22 - m00 * m12 * m21 ) if deterimnant == 1: return False elif deterimnant == -1: return True else: raise RuntimeWarning(f"Degenerate coordinate system: {coords} with ddeterimnant {deterimnant}") def doclines(coords: ViewCoordinates) -> list[str]: docs = [f"X={coords.x}, Y={coords.y}, Z={coords.z}"] if is_left_handed(coords): docs.append("") docs.append( "⚠️ This is a left-handed coordinate system, which is [not yet supported by Rerun](https://github.com/rerun-io/rerun/issues/5032).", ) return docs def generate_view_permutations() -> Iterable[ViewCoordinates]: D1 = ["Up", "Down"] D2 = ["Left", "Right"] D3 = ["Forward", "Back"] for i in D1: for j in D2: for k in D3: for x, y, z in itertools.permutations([i, j, k]): name = f"{x[0]}{y[0]}{z[0]}" yield ViewCoordinates(name, x, y, z) def generate_up_handed_permutations() -> Iterable[ViewCoordinates]: return [ ViewCoordinates(name="RIGHT_HAND_X_UP", x="Up", y="Right", z="Forward"), ViewCoordinates(name="RIGHT_HAND_X_DOWN", x="Down", y="Right", z="Back"), ViewCoordinates(name="RIGHT_HAND_Y_UP", x="Right", y="Up", z="Back"), ViewCoordinates(name="RIGHT_HAND_Y_DOWN", x="Right", y="Down", z="Forward"), ViewCoordinates(name="RIGHT_HAND_Z_UP", x="Right", y="Forward", z="Up"), ViewCoordinates(name="RIGHT_HAND_Z_DOWN", x="Right", y="Back", z="Down"), ViewCoordinates(name="LEFT_HAND_X_UP", x="Up", y="Right", z="Back"), ViewCoordinates(name="LEFT_HAND_X_DOWN", x="Down", y="Right", z="Forward"), ViewCoordinates(name="LEFT_HAND_Y_UP", x="Right", y="Up", z="Forward"), ViewCoordinates(name="LEFT_HAND_Y_DOWN", x="Right", y="Down", z="Back"), ViewCoordinates(name="LEFT_HAND_Z_UP", x="Right", y="Back", z="Up"), ViewCoordinates(name="LEFT_HAND_Z_DOWN", x="Right", y="Forward", z="Down"), ] ################################################################################ # Rust Archetype RUST_ARCHETYPE_EXTENSION_FILE = "crates/store/re_sdk_types/src/archetypes/view_coordinates_ext.rs" def rust_arch_decl(coords: ViewCoordinates) -> str: docstring = "\n".join(doclines(coords)) return f'define_coordinates!("{docstring}", {coords.name} => ({coords.x}, {coords.y}, {coords.z}));\n' def gen_rust_arch_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --rust`\n") lines.extend(rust_arch_decl(v) for v in generate_view_permutations()) lines.extend(rust_arch_decl(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") return lines ################################################################################ # Rust Component RUST_COMPONENT_EXTENSION_FILE = "crates/store/re_sdk_types/src/components/view_coordinates_ext.rs" def rust_cmp_decl(coords: ViewCoordinates) -> str: docstring = "\n".join(doclines(coords)) return f'define_coordinates!("{docstring}", {coords.name} => ({coords.x}, {coords.y}, {coords.z}));\n' def gen_rust_cmp_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --rust`\n") lines.extend(rust_cmp_decl(v) for v in generate_view_permutations()) lines.extend(rust_cmp_decl(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") return lines ################################################################################ # Python Archetype PYTHON_ARCHETYPE_EXTENSION_FILE = "rerun_py/rerun_sdk/rerun/archetypes/view_coordinates_ext.py" def py_arch_decl(coords: ViewCoordinates) -> str: return f"{coords.name}: ViewCoordinates = None # type: ignore[assignment]\n" def gen_py_arch_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"# {BEGIN_MARKER.format(key)}\n") lines.append(f"# This section is generated by running `{SCRIPT_PATH} --python`\n") lines.append("# The following declarations are replaced in `deferred_patch_class`.\n") lines.extend(py_arch_decl(v) for v in generate_view_permutations()) lines.extend(py_arch_decl(v) for v in generate_up_handed_permutations()) lines.append(f"# {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines def py_arch_def(coords: ViewCoordinates) -> str: return f"cls.{coords.name} = Component.{coords.name}\n" def gen_py_arch_def() -> list[str]: key = "definitions" lines = [] lines.append(f"# {BEGIN_MARKER.format(key)}\n") lines.append(f"# This section is generated by running `{SCRIPT_PATH} --python`\n") lines.extend(py_arch_def(v) for v in generate_view_permutations()) lines.extend(py_arch_def(v) for v in generate_up_handed_permutations()) lines.append(f"# {END_MARKER.format(key)}\n") lines = [" " * 8 + line for line in lines] return lines ################################################################################ # Python Component PYTHON_COMPONENT_EXTENSION_FILE = "rerun_py/rerun_sdk/rerun/components/view_coordinates_ext.py" def py_cmp_decl(coords: ViewCoordinates) -> str: docstring = "\n".join(doclines(coords)) return f'{coords.name}: ViewCoordinates = None # type: ignore[assignment]\n """{docstring}"""\n\n' def gen_py_cmp_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"# {BEGIN_MARKER.format(key)}\n") lines.append(f"# This section is generated by running `{SCRIPT_PATH} --python`\n") lines.append("# The following declarations are replaced in `deferred_patch_class`.\n") lines.extend(py_cmp_decl(v) for v in generate_view_permutations()) lines.extend(py_cmp_decl(v) for v in generate_up_handed_permutations()) lines.append(f"# {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines def py_cmp_def(coords: ViewCoordinates) -> str: return f"cls.{coords.name} = cls([cls.ViewDir.{coords.x}, cls.ViewDir.{coords.y}, cls.ViewDir.{coords.z}])\n" def gen_py_cmp_def() -> list[str]: key = "definitions" lines = [] lines.append(f"# {BEGIN_MARKER.format(key)}\n") lines.append(f"# This section is generated by running `{SCRIPT_PATH} --python`\n") lines.extend(py_cmp_def(v) for v in generate_view_permutations()) lines.extend(py_cmp_def(v) for v in generate_up_handed_permutations()) lines.append(f"# {END_MARKER.format(key)}\n") lines = [" " * 8 + line for line in lines] return lines ################################################################################ # CPP Archetype CPP_ARCHETYPE_EXTENSION_FILE = "rerun_cpp/src/rerun/archetypes/view_coordinates_ext.cpp" def cpp_arch_decl(coords: ViewCoordinates) -> str: docstring = "".join(f"/// {docline}\n" for docline in doclines(coords)) return f"{docstring}RERUN_SDK_EXPORT static const rerun::archetypes::ViewCoordinates {coords.name};\n\n" def gen_cpp_arch_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --cpp`\n") lines.extend(cpp_arch_decl(v) for v in generate_view_permutations()) lines.extend(cpp_arch_decl(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines def cpp_arch_def(coords: ViewCoordinates) -> str: return ( f"const ViewCoordinates ViewCoordinates::{coords.name} = ViewCoordinates(\n" f"rerun::components::ViewCoordinates::{coords.name}\n" ");\n" ) def gen_cpp_arch_def() -> list[str]: key = "definitions" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --cpp`\n") lines.extend(cpp_arch_def(v) for v in generate_view_permutations()) lines.extend(cpp_arch_def(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines ################################################################################ # CPP Component CPP_COMPONENT_EXTENSION_FILE = "rerun_cpp/src/rerun/components/view_coordinates_ext.cpp" def cpp_cmp_decl(coords: ViewCoordinates) -> str: docstring = "".join(f"/// {docline}\n" for docline in doclines(coords)) return f"{docstring}RERUN_SDK_EXPORT static const rerun::components::ViewCoordinates {coords.name};\n\n" def gen_cpp_cmp_decl() -> list[str]: key = "declarations" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --cpp`\n") lines.extend(cpp_cmp_decl(v) for v in generate_view_permutations()) lines.extend(cpp_cmp_decl(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines def cpp_cmp_def(coords: ViewCoordinates) -> str: return ( f"const ViewCoordinates ViewCoordinates::{coords.name} = ViewCoordinates(\n" f"rerun::components::ViewCoordinates::{coords.x}, rerun::components::ViewCoordinates::{coords.y}, rerun::components::ViewCoordinates::{coords.z}\n" ");\n" ) def gen_cpp_cmp_def() -> list[str]: key = "definitions" lines = [] lines.append(f"// {BEGIN_MARKER.format(key)}\n") lines.append(f"// This section is generated by running `{SCRIPT_PATH} --cpp`\n") lines.extend(cpp_cmp_def(v) for v in generate_view_permutations()) lines.extend(cpp_cmp_def(v) for v in generate_up_handed_permutations()) lines.append(f"// {END_MARKER.format(key)}\n") lines = [" " * 4 + line for line in lines] return lines ################################################################################ def show_preview(lines: list[str]) -> None: print("".join(lines)) def patch_file(filename: str, lines: list[str], key: str) -> None: contents = open(filename, encoding="utf8").readlines() start_line = next((i for i, line in enumerate(contents) if BEGIN_MARKER.format(key) in line), None) end_line = next((i for i, line in enumerate(contents) if END_MARKER.format(key) in line), None) if (start_line is None) or (end_line is None): raise Exception("Could not find the generated section in the file.") new_contents = contents[:start_line] + lines + contents[end_line + 1 :] open(filename, "w", encoding="utf8").writelines(new_contents) ################################################################################ def process_file(preview: bool, filename: str, decl_lines: list[str] | None, def_lines: list[str] | None) -> None: if preview: if def_lines is not None: print(f"Preview of {filename}: definitions") show_preview(def_lines) if decl_lines is not None: print(f"Preview of {filename}: declarations") show_preview(decl_lines) else: if def_lines is not None: patch_file(filename, def_lines, "definitions") if decl_lines is not None: patch_file(filename, decl_lines, "declarations") def main() -> None: parser = argparse.ArgumentParser(description="Modify the ViewCoordinate archetypes.") parser.add_argument( "--rust", action="store_true", default=False, help="Generate the rust code for the view coordinates.", ) parser.add_argument( "--python", action="store_true", default=False, help="Generate the python code for the view coordinates.", ) parser.add_argument( "--cpp", action="store_true", default=False, help="Generate the cpp code for the view coordinates.", ) parser.add_argument( "--preview", action="store_true", default=False, help="Just print the preview of the generated sections", ) args = parser.parse_args() if args.rust: process_file(args.preview, RUST_ARCHETYPE_EXTENSION_FILE, gen_rust_arch_decl(), None) process_file(args.preview, RUST_COMPONENT_EXTENSION_FILE, gen_rust_cmp_decl(), None) if args.python: process_file(args.preview, PYTHON_ARCHETYPE_EXTENSION_FILE, gen_py_arch_decl(), gen_py_arch_def()) process_file(args.preview, PYTHON_COMPONENT_EXTENSION_FILE, gen_py_cmp_decl(), gen_py_cmp_def()) if args.cpp: process_file(args.preview, CPP_ARCHETYPE_EXTENSION_FILE, gen_cpp_arch_decl(), gen_cpp_arch_def()) process_file(args.preview, CPP_COMPONENT_EXTENSION_FILE, gen_cpp_cmp_decl(), gen_cpp_cmp_def()) if __name__ == "__main__": main()