Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dabc743
Add Easy GUI (M1): guided raw recording -> lidar odometry app
MartinMokros Oct 17, 2026
41a2f34
Add Easy GUI export (M2): LAS 1.4 point cloud with near points, traje…
MartinMokros Oct 17, 2026
187aa19
Add Easy GUI place_by_raw_time export option (default off); add CLAUD…
MartinMokros Oct 17, 2026
935bf03
Easy GUI export: add step 1's skipped gap points; point_origin replac…
MartinMokros Oct 17, 2026
c90c46d
Easy GUI: move design notes to apps/easy_gui/docs/DESIGN.md; stop tra…
MartinMokros Oct 18, 2026
da696da
Add HDMAPPING_EASY_GUI_ONLY build switch (default off)
MartinMokros Oct 18, 2026
4d556e3
Easy GUI: user-facing name HDMapping Easy Forestry; macOS 13.0 target…
MartinMokros Oct 18, 2026
8b8bc83
Easy Forestry: user README and design notes update
MartinMokros Oct 18, 2026
ba81e5f
README: add a banner pointing to HDMapping Easy Forestry
MartinMokros Oct 18, 2026
281fa81
Rename user-facing name to Easy Forest HDMapping
MartinMokros Oct 18, 2026
86ae366
Easy Forest HDMapping: macOS and Windows release workflows
MartinMokros Oct 18, 2026
2788291
Easy Forest Windows workflow: default Visual Studio generator, latest…
MartinMokros Oct 18, 2026
095a4fa
easy_gui_tests: fix cmd.exe stderr test (no space before the redirect…
MartinMokros Oct 18, 2026
2d6c331
Easy Forest DESIGN.md: Windows build results
MartinMokros Oct 18, 2026
8cc5a03
Easy Forest HDMapping: own version 0.1.0 next to HDMapping's
MartinMokros Oct 18, 2026
1bb7df5
Easy Forest README: download from the releases page; Windows status
MartinMokros Oct 18, 2026
8e332c0
Easy Forest release workflows: 0.x releases are pre-releases; tag mus…
MartinMokros Oct 18, 2026
6ba8600
easy_gui: resume into an output folder that holds only an unfinished …
MartinMokros Oct 18, 2026
cc7d703
easy_gui: batch core (discovery, output names, done markers, disk che…
MartinMokros Oct 18, 2026
45792e9
easy_gui: --batch <parent folder> <output folder>
MartinMokros Oct 18, 2026
b3269fd
easy_gui: "Several recordings" mode in the window
MartinMokros Oct 18, 2026
eb35b7b
Easy Forest HDMapping 0.2.0: batch mode docs
MartinMokros Oct 18, 2026
4a5282c
Easy Forest HDMapping 0.2.0: screenshots of batch mode; DESIGN.md: Wi…
MartinMokros Oct 19, 2026
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
137 changes: 137 additions & 0 deletions .github/workflows/easy-forest-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Easy Forest HDMapping release build for macOS (Apple silicon, macOS 13.0 and newer).
# Separate from the upstream macos.yml: builds only easy_gui + lidar_odometry_step_1 (HDMAPPING_EASY_GUI_ONLY) and
# packages them as "Easy Forest HDMapping.app" in a DMG. The packaging steps are scripts in
# apps/easy_gui/packaging/macos, so they can be run and debugged locally the same way.
name: Easy Forest HDMapping macOS

on:
push:
tags: ["easy-forest-v*"]
workflow_dispatch:

permissions:
contents: write # attach the DMG to the release on a tag

env:
MIN_MACOS: "13.0"
# Picked up by every CMake project (PROJ, the FetchContent raylib) and by clang directly.
MACOSX_DEPLOYMENT_TARGET: "13.0"

jobs:
build:
runs-on: macos-15

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- name: Set version
run: |
if [[ "$GITHUB_REF" == refs/tags/easy-forest-v* ]]; then
echo "VERSION=${GITHUB_REF_NAME#easy-forest-v}" >> "$GITHUB_ENV"
# The app shows EASY_FOREST_VERSION from CMake; it must be the tag's version.
cmake_version=$(sed -n 's/^set(EASY_FOREST_VERSION "\(.*\)")$/\1/p' apps/easy_gui/CMakeLists.txt)
if [[ "$cmake_version" != "${GITHUB_REF_NAME#easy-forest-v}" ]]; then
echo "::error::Tag $GITHUB_REF_NAME does not match EASY_FOREST_VERSION $cmake_version in apps/easy_gui/CMakeLists.txt"
exit 1
fi
else
echo "VERSION=$(git describe --tags --always)" >> "$GITHUB_ENV"
fi

- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.0.0"

# Homebrew's PROJ is built for the runner's macOS; build a static one for MIN_MACOS instead (see the script).
- name: Cache PROJ
id: proj-cache
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/proj-install
key: proj-macos-${{ env.MIN_MACOS }}-${{ runner.arch }}-${{ hashFiles('apps/easy_gui/packaging/macos/build_proj.sh') }}

- name: Build PROJ
if: steps.proj-cache.outputs.cache-hit != 'true'
run: apps/easy_gui/packaging/macos/build_proj.sh "$RUNNER_TEMP/proj-install" "$RUNNER_TEMP/proj-work"

# TBB_DISABLE_HWLOC_AUTOMATIC_SEARCH: the bundled oneTBB would otherwise pick up a Homebrew hwloc for tbbbind.
- name: Configure
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET="$MIN_MACOS" \
-DHDMAPPING_EASY_GUI_ONLY=ON \
-DBUILD_TESTING=ON \
-DFREEGLUT_COCOA=ON \
-DHD_CPU_OPTIMIZATION=ARM \
-DTBB_DISABLE_HWLOC_AUTOMATIC_SEARCH=ON \
-DPROJ_DIR="$RUNNER_TEMP/proj-install/lib/cmake/proj"

# A static library built for a newer macOS only shows up as a linker warning (the executables still say
# minos 13.0), so treat that warning as an error.
- name: Build
run: |
set -o pipefail
cmake --build build --config Release -j"$(sysctl -n hw.ncpu)" 2>&1 | tee build.log
if grep -i "built for newer macOS version" build.log; then
echo "::error::Something was linked that was built for a newer macOS than $MIN_MACOS"
exit 1
fi

- name: Test
working-directory: build
run: ctest -C Release --output-on-failure

# Also checks minos <= MIN_MACOS for every bundled binary and fails otherwise.
- name: Create app bundle
run: python3 apps/easy_gui/packaging/macos/make_app.py build/bin dist --version "$VERSION" --min-macos "$MIN_MACOS"

- name: Smoke test the bundle
run: |
set +e
"dist/Easy Forest HDMapping.app/Contents/MacOS/easy_gui" --headless > usage.txt 2>&1
rc=$?
set -e
cat usage.txt
# Exit code 2 + usage text = dyld loaded every bundled library and main() ran.
test "$rc" -eq 2 && grep -q "usage:" usage.txt

- name: Create DMG
run: apps/easy_gui/packaging/macos/make_dmg.sh "dist/Easy Forest HDMapping.app" dist/EasyForestHDMapping-mac.dmg

- name: Sizes
run: |
{
echo "### Easy Forest HDMapping macOS $VERSION"
echo '```'
ls -l dist/EasyForestHDMapping-mac.dmg
du -sh "dist/Easy Forest HDMapping.app"
find "dist/Easy Forest HDMapping.app/Contents" -type f -exec ls -l {} \; | awk '{printf "%10d %s\n", $5, substr($0, index($0, $9))}'
echo '```'
} | tee -a "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@v4
with:
name: EasyForestHDMapping-mac-${{ env.VERSION }}
path: dist/EasyForestHDMapping-mac.dmg
compression-level: 0 # already compressed
retention-days: 90

# The macOS and Windows workflows both run on the same tag: whichever gets here first creates the release.
# 0.x versions and versions with a suffix (0.2.0-rc1) are pre-releases. Created with GITHUB_TOKEN, so the
# release does not trigger other workflows (upstream windows.yml runs on release: published).
- name: Attach to GitHub release
if: startsWith(github.ref, 'refs/tags/easy-forest-v')
env:
GH_TOKEN: ${{ github.token }}
run: |
PRERELEASE=""
if [[ "$VERSION" == 0.* || "$VERSION" == *-* ]]; then PRERELEASE="--prerelease"; fi
gh release view "$GITHUB_REF_NAME" > /dev/null 2>&1 \
|| gh release create "$GITHUB_REF_NAME" --verify-tag --title "Easy Forest HDMapping $VERSION" $PRERELEASE \
--notes "Installation and use: [apps/easy_gui/README.md](https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_REF_NAME}/apps/easy_gui/README.md)" \
|| gh release view "$GITHUB_REF_NAME" > /dev/null
gh release upload "$GITHUB_REF_NAME" dist/EasyForestHDMapping-mac.dmg --clobber
111 changes: 111 additions & 0 deletions .github/workflows/easy-forest-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Easy Forest HDMapping release build for Windows (x64).
# Separate from the upstream windows.yml: builds only easy_gui + lidar_odometry_step_1 (HDMAPPING_EASY_GUI_ONLY) and
# packages them with every DLL they need, the MSVC runtime included, as one zip (apps/easy_gui/packaging/windows).
name: Easy Forest HDMapping Windows

on:
push:
tags: ["easy-forest-v*"]
workflow_dispatch:

permissions:
contents: write # attach the zip to the release on a tag

jobs:
build:
runs-on: windows-latest

defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- name: Set version
run: |
if [[ "$GITHUB_REF" == refs/tags/easy-forest-v* ]]; then
echo "VERSION=${GITHUB_REF_NAME#easy-forest-v}" >> "$GITHUB_ENV"
# The app shows EASY_FOREST_VERSION from CMake; it must be the tag's version.
cmake_version=$(sed -n 's/^set(EASY_FOREST_VERSION "\(.*\)")$/\1/p' apps/easy_gui/CMakeLists.txt)
if [[ "$cmake_version" != "${GITHUB_REF_NAME#easy-forest-v}" ]]; then
echo "::error::Tag $GITHUB_REF_NAME does not match EASY_FOREST_VERSION $cmake_version in apps/easy_gui/CMakeLists.txt"
exit 1
fi
else
echo "VERSION=$(git describe --tags --always)" >> "$GITHUB_ENV"
fi

# Latest CMake: it has to know the generator of whatever Visual Studio the runner image ships.
- uses: lukka/get-cmake@latest

# HD_CPU_OPTIMIZATION=GENERIC: AUTO adds /arch:AVX2 when the runner's CPU has it, and the release must also run
# on PCs without AVX2.
- name: Configure
run: |
cmake -B build -A x64 \
-DHDMAPPING_EASY_GUI_ONLY=ON \
-DBUILD_TESTING=ON \
-DHD_CPU_OPTIMIZATION=GENERIC \
-DTBB_DISABLE_HWLOC_AUTOMATIC_SEARCH=ON

- name: Build
run: cmake --build build --config Release --parallel

- name: Test
working-directory: build
run: ctest -C Release --output-on-failure

- name: Create folder and zip
run: |
python apps/easy_gui/packaging/windows/make_zip.py build/bin/Release \
"dist/Easy Forest HDMapping" dist/EasyForestHDMapping-windows.zip \
--search-dir build/3rd_binary/PROJ/install_proj/bin

- name: Smoke test the folder
run: |
# easy_gui.exe is a windowed program, so check from its own folder that it and lidar_odometry_step_1.exe
# load all their DLLs: run with bad arguments and expect the usage exit code 2.
set +e
"dist/Easy Forest HDMapping/easy_gui.exe" --headless
rc=$?
set -e
echo "easy_gui.exe exit code: $rc"
test "$rc" -eq 2

- name: Sizes
run: |
{
echo "### Easy Forest HDMapping Windows $VERSION"
echo '```'
ls -l dist/EasyForestHDMapping-windows.zip
du -sh "dist/Easy Forest HDMapping"
ls -l "dist/Easy Forest HDMapping"
echo '```'
} | tee -a "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@v4
with:
name: EasyForestHDMapping-windows-${{ env.VERSION }}
path: dist/EasyForestHDMapping-windows.zip
compression-level: 0 # already compressed
retention-days: 90

# The macOS and Windows workflows both run on the same tag: whichever gets here first creates the release.
# 0.x versions and versions with a suffix (0.2.0-rc1) are pre-releases. Created with GITHUB_TOKEN, so the
# release does not trigger other workflows (upstream windows.yml runs on release: published).
- name: Attach to GitHub release
if: startsWith(github.ref, 'refs/tags/easy-forest-v')
env:
GH_TOKEN: ${{ github.token }}
run: |
PRERELEASE=""
if [[ "$VERSION" == 0.* || "$VERSION" == *-* ]]; then PRERELEASE="--prerelease"; fi
gh release view "$GITHUB_REF_NAME" > /dev/null 2>&1 \
|| gh release create "$GITHUB_REF_NAME" --verify-tag --title "Easy Forest HDMapping $VERSION" $PRERELEASE \
--notes "Installation and use: [apps/easy_gui/README.md](https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_REF_NAME}/apps/easy_gui/README.md)" \
|| gh release view "$GITHUB_REF_NAME" > /dev/null
gh release upload "$GITHUB_REF_NAME" dist/EasyForestHDMapping-windows.zip --clobber
51 changes: 44 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
cmake_minimum_required(VERSION 4.0.0)

# Easy Forest HDMapping release builds (HDMAPPING_EASY_GUI_ONLY) must run on older macOS versions than the
# build machine's, which is the default minimum. Must be set before project(). Pass -DCMAKE_OSX_DEPLOYMENT_TARGET to override.
if(HDMAPPING_EASY_GUI_ONLY AND CMAKE_HOST_APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "Minimum macOS version" FORCE)
endif()

project(hd-mapping VERSION 0.105.0)

# Common settings
Expand Down Expand Up @@ -32,6 +38,7 @@ option(BUILD_WITH_BUNDLED_LIBLASZIP "Build with bundled Lib LASZIP" ON)
option(BUILD_WITH_BUNDLED_ONETBB "Build with bundled oneTBB" ON)
option(BUILD_WITH_PYBIND "Enable pybind11 bindings" OFF)
option(HDMAPPING_ASSERT_ALWAYS_ABORT "Make HDMAPPING_ASSERT() abort on failure even in release (NDEBUG) builds" OFF)
option(HDMAPPING_EASY_GUI_ONLY "Build only easy_gui, lidar_odometry_step_1 and their dependencies (plus easy_gui_tests with BUILD_TESTING)" OFF)

add_definitions(-D_USE_MATH_DEFINES)

Expand Down Expand Up @@ -107,37 +114,65 @@ include(cmake/dependencies.cmake)
# unconditionally: apps/multi_view_tls_registration (step2) is raylib-based.
include(cmake/raylib.cmake)

# HDMAPPING_EASY_GUI_ONLY: the library directories are still added (easy_gui and
# step 1 link some of their targets), but EXCLUDE_FROM_ALL, so a target in them
# is only built when easy_gui or lidar_odometry_step_1 needs it. All other apps,
# tests and optional tool sets are skipped. Off by default: nothing changes.
if(HDMAPPING_EASY_GUI_ONLY)
set(HDMAPPING_EASY_GUI_ONLY_EXCLUDE EXCLUDE_FROM_ALL)
foreach(_hd_skipped BUILD_WITH_CLI_TOOLS BUILD_WITH_HD_MAPPER_APPLICATION BUILD_WITH_UTILITY_APPLICATION)
if(${_hd_skipped})
message(STATUS "HDMAPPING_EASY_GUI_ONLY: ignoring ${_hd_skipped}")
endif()
endforeach()
endif()

# ============================================================================
# Core Library
# ============================================================================
add_subdirectory(core)
add_subdirectory(calib_core)
add_subdirectory(raylib_widgets)
add_subdirectory(core ${HDMAPPING_EASY_GUI_ONLY_EXCLUDE})
add_subdirectory(calib_core ${HDMAPPING_EASY_GUI_ONLY_EXCLUDE})
add_subdirectory(raylib_widgets ${HDMAPPING_EASY_GUI_ONLY_EXCLUDE})

# ============================================================================
# Unit tests (opt-in: cmake -DBUILD_TESTING=ON)
# ============================================================================
option(BUILD_TESTING "Build HDMapping unit tests" OFF)
if(BUILD_TESTING)
if(BUILD_TESTING AND HDMAPPING_EASY_GUI_ONLY)
enable_testing()
add_subdirectory(apps/easy_gui/tests)
elseif(BUILD_TESTING)
enable_testing()
add_subdirectory(shared/tests)
add_subdirectory(calib_core/tests)
add_subdirectory(apps/lidar_odometry_step_1/tests)
add_subdirectory(rosbags/tests)
add_subdirectory(apps/easy_gui/tests)
endif()

set(CORE_LIBRARIES core)
set(GUI_LIBRARIES imgui imguizmo implot)

if(BUILD_WITH_CLI_TOOLS)
if(HDMAPPING_EASY_GUI_ONLY)
# The step 1 directory also defines the drag-and-drop precision forestry app.
# EXCLUDE_FROM_ALL skips it, together with the directory's install rules, so
# lidar_odometry_step_1 is put back into "all" and installed from here.
add_subdirectory(apps/lidar_odometry_step_1 EXCLUDE_FROM_ALL)
set_target_properties(lidar_odometry_step_1 PROPERTIES EXCLUDE_FROM_ALL OFF)
hdmapping_install_app(lidar_odometry_step_1)
add_subdirectory(apps/easy_gui)
endif()

if(BUILD_WITH_CLI_TOOLS AND NOT HDMAPPING_EASY_GUI_ONLY)
add_subdirectory(apps/console_tools)
endif()

if(BUILD_WITH_HD_MAPPER_APPLICATION)
if(BUILD_WITH_HD_MAPPER_APPLICATION AND NOT HDMAPPING_EASY_GUI_ONLY)
add_subdirectory(apps/hd_mapper)
endif()

# Applications for typical workflow - see images/workflow.png
if(NOT HDMAPPING_EASY_GUI_ONLY)
add_subdirectory(apps/mandeye_raw_data_viewer)
add_subdirectory(apps/lidar_odometry_step_1)
add_subdirectory(apps/mandeye_single_session_viewer)
Expand All @@ -147,8 +182,10 @@ add_subdirectory(apps/multi_session_registration)
add_subdirectory(apps/multi_session_registration_legacy)
add_subdirectory(apps/session_uncertainty_calculation)
add_subdirectory(apps/session_to_session_rigid_registration)
add_subdirectory(apps/easy_gui)
endif()

if(BUILD_WITH_UTILITY_APPLICATION)
if(BUILD_WITH_UTILITY_APPLICATION AND NOT HDMAPPING_EASY_GUI_ONLY)
add_subdirectory(apps/split_multi_livox)
add_subdirectory(apps/precision_forestry_tools)
add_subdirectory(apps/compare_trajectories)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
> **New: Easy Forest HDMapping.** One window that turns a raw Mandeye recording folder into a point cloud (LAZ) and a
> trajectory (CSV) with the precision forestry settings, no other steps needed. See
> [apps/easy_gui/README.md](apps/easy_gui/README.md).

# Open-source (Windows, Linux and macOS), open hardware hand-held/wearable/mountable mobile mapping system for large-scale surveys:
This is open-source software for large-scale 3D mapping using an open-hardware hand-held/wearable/mountable measurement device available at https://github.com/JanuszBedkowski/mandeye_controller.
We provide an end-to-end mobile mapping framework that does not require any installation, including:
Expand Down
Loading