Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions .github/workflows/trx-cpp-tests.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
name: trx-cpp tests

on:
push:
paths:
- "**"
pull_request:
paths:
- "**"

jobs:
build-and-test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
boost:
- "ON"
- "OFF"
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -25,6 +28,9 @@ jobs:
libeigen3-dev \
nlohmann-json3-dev \
libspdlog-dev
if [ "${{ matrix.boost }}" = "ON" ]; then
sudo apt-get install -y libboost-filesystem-dev libboost-system-dev
fi

- name: Fetch mio
run: |
Expand All @@ -48,10 +54,11 @@ jobs:
-DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/deps/googletest/install
cmake --build deps/googletest/build --target install

- name: Configure trx-cpp
- name: Configure trx-cpp (Boost ${{ matrix.boost }})
run: |
cmake -S . -B build \
-DTRX_BUILD_TESTS=ON \
-DTRX_USE_BOOST_FILESYSTEM=${{ matrix.boost }} \
-DMIO_INCLUDE_DIR=${GITHUB_WORKSPACE}/deps/mio/include \
-DGTest_DIR=${GITHUB_WORKSPACE}/deps/googletest/install/lib/cmake/GTest \
-DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/deps/libzip/install
Expand All @@ -64,6 +71,12 @@ jobs:

conan-create:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
boost:
- "True"
- "False"
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -78,9 +91,9 @@ jobs:
python3 -m pip install --upgrade pip
python3 -m pip install "conan>=2.0,<3.0"

- name: Conan create (with tests)
- name: Conan create (with tests, Boost ${{ matrix.boost }})
env:
CONAN_HOME: ${{ runner.temp }}/.conan2
run: |
conan profile detect --force
conan create . --build=missing -o with_tests=True -s build_type=Release
conan create . --build=missing -o with_tests=True -o with_boost=${{ matrix.boost }} -s build_type=Release
15 changes: 14 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ project(trx VERSION 0.1.0)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 11)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Debug)
endif()

option(TRX_USE_CONAN "Should Conan package manager be used?" ON)
option(TRX_USE_BOOST_FILESYSTEM "Use Boost filesystem if available" ON)
option(TRX_BUILD_TESTS "Build trx tests" ON)

find_package(libzip REQUIRED)
Expand All @@ -39,6 +40,9 @@ if (NOT TARGET Eigen3::Eigen AND EXISTS "${EIGEN3_INCLUDE_DIR}")
endif()
find_package(nlohmann_json CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
if(TRX_USE_BOOST_FILESYSTEM)
find_package(Boost QUIET COMPONENTS filesystem system)
endif()
find_package(mio CONFIG QUIET)
if(TARGET mio::mio)
set(TRX_HAVE_MIO_TARGET ON)
Expand Down Expand Up @@ -76,6 +80,15 @@ TARGET_LINK_LIBRARIES(trx
spdlog::spdlog
$<$<BOOL:${TRX_HAVE_MIO_TARGET}>:mio::mio>
)
if(TRX_USE_BOOST_FILESYSTEM AND Boost_FOUND)
target_compile_definitions(trx PUBLIC TRX_USE_BOOST_FILESYSTEM)
if(TARGET Boost::filesystem)
target_link_libraries(trx PUBLIC Boost::filesystem Boost::system)
else()
target_include_directories(trx PUBLIC ${Boost_INCLUDE_DIRS})
target_link_libraries(trx PUBLIC ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
endif()
endif()
target_include_directories(trx
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand Down
130 changes: 121 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,130 @@
# TRX-cpp

The C++ implementations to the memory-mapped tractography file format.
TRX-cpp is a C++11 library for reading, writing, and memory-mapping the TRX
tractography file format (zip archives or on-disk directories of memmaps).

## Installation
### Dependencies
- c++11 compiler / cmake
## Dependencies

Required:
- C++11 compiler and CMake (>= 3.10)
- libzip
- nlohmann::json
- Eigen3
- spdlog
- GTest (optional)
- mio (header-only memory-mapped IO)

Optional:
- GTest (for building tests)
- Boost.Filesystem + Boost.System (optional filesystem shim)

## Build and Install

### Quick build (no tests)

```
cmake -S . -B build
cmake --build build
```

### Build with tests

```
cmake -S . -B build \
-DTRX_BUILD_TESTS=ON \
-DGTest_DIR=/path/to/GTestConfig.cmake
cmake --build build
ctest --test-dir build --output-on-failure
```

### mio include path

If `mio` is not available via your system include paths, point CMake at it:

```
cmake -S . -B build -DMIO_INCLUDE_DIR=/path/to/mio/include
```

### Filesystem shim

By default, the build will try to use Boost.Filesystem if it is available. You
can force this behavior:

```
# Prefer Boost if available (default)
cmake -S . -B build -DTRX_USE_BOOST_FILESYSTEM=ON

# Force the built-in lightweight shim
cmake -S . -B build -DTRX_USE_BOOST_FILESYSTEM=OFF
```

## Usage Examples

All examples assume:

```
#include <trx/trx.h>

using namespace trxmmap;
```

### Read a TRX zip and inspect data

```
TrxFile<half> *trx = load_from_zip<half>("tracks.trx");

// Access streamlines: vertices are stored as an Eigen matrix
const auto num_vertices = trx->streamlines->_data.size() / 3;
const auto num_streamlines = trx->streamlines->_offsets.size() - 1;

std::cout << "Vertices: " << num_vertices << "\n";
std::cout << "Streamlines: " << num_streamlines << "\n";
std::cout << "First vertex (x,y,z): "
<< trx->streamlines->_data(0, 0) << ","
<< trx->streamlines->_data(0, 1) << ","
<< trx->streamlines->_data(0, 2) << "\n";

// Data-per-streamline and data-per-vertex are stored in maps
for (const auto &kv : trx->data_per_streamline) {
std::cout << "DPS '" << kv.first << "' elements: "
<< kv.second->_matrix.size() << "\n";
}
for (const auto &kv : trx->data_per_vertex) {
std::cout << "DPV '" << kv.first << "' elements: "
<< kv.second->_data.size() << "\n";
}

trx->close(); // cleans up temporary on-disk data
delete trx;
```

### Read from an on-disk TRX directory

```
TrxFile<float> *trx = load_from_directory<float>("/path/to/trx_dir");
std::cout << "Header JSON:\n" << trx->header.dump(2) << "\n";
trx->close();
delete trx;
```

### Write a TRX file

You can modify a loaded `TrxFile` and save it to a new archive:

```
TrxFile<half> *trx = load_from_zip<half>("tracks.trx");

// Example: update header metadata
trx->header["COMMENT"] = "saved by trx-cpp";

// Save with no compression (ZIP_CM_STORE) or another libzip compression level
save(*trx, "tracks_copy.trx", ZIP_CM_STORE);

trx->close();
delete trx;
```

### Installing
`cmake . && make`
### Notes on memory mapping

## How to use
COMING SOON
`TrxFile` uses memory-mapped arrays under the hood for large datasets. The
`close()` method cleans up any temporary folders created during zip extraction.
If you skip `close()`, temporary directories may be left behind.
13 changes: 13 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ class TrxCppConan(ConanFile):
"shared": [True, False],
"fPIC": [True, False],
"with_tests": [True, False],
"with_boost": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"with_tests": False,
"with_boost": True,
}
generators = ("CMakeDeps",)
exports_sources = (
Expand All @@ -42,6 +44,8 @@ def requirements(self):
self.requires("nlohmann_json/3.11.3")
self.requires("eigen/3.4.0")
self.requires("spdlog/1.12.0")
if self.options.with_boost:
self.requires("boost/1.83.0")

def build_requirements(self):
if self.options.with_tests:
Expand All @@ -65,6 +69,10 @@ def generate(self):
tc = CMakeToolchain(self)
mio_include = os.path.join(self.source_folder, "mio", "include")
tc.variables["MIO_INCLUDE_DIR"] = mio_include
tc.variables["CMAKE_CXX_STANDARD"] = 11
tc.variables["CMAKE_CXX_STANDARD_REQUIRED"] = "ON"
tc.variables["CMAKE_CXX_EXTENSIONS"] = "OFF"
tc.variables["TRX_USE_BOOST_FILESYSTEM"] = "ON" if self.options.with_boost else "OFF"
tc.generate()

def build(self):
Expand Down Expand Up @@ -119,6 +127,11 @@ def package_info(self):
"nlohmann_json::nlohmann_json",
"eigen::Eigen3::Eigen",
]
if self.options.with_boost:
self.cpp_info.components["trx"].requires.extend([
"boost::filesystem",
"boost::system",
])
extra_includes = []
for dep_name in ("nlohmann_json", "eigen"):
dep = self.dependencies.get(dep_name)
Expand Down
Loading