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
45 changes: 45 additions & 0 deletions apps/camera_lidar_calibration/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,38 @@ static bool shiftHeld()
return IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
}

// Loads one dropped file by extension -- the same extension -> action mapping as main.cpp's
// preloadByExt(), but applied immediately (the window is already running) instead of queued for
// App::run()'s startup pass. `sawCloud` tracks whether a *.laz/*.las has already landed in this
// drop batch, so the first one replaces the cloud (like UI::actionOpenPointCloud) and later ones
// merge into it (like UI::actionAddPointCloud) -- mirroring how a multi-file drop of point clouds
// mixes "open" and "add" semantics for a natural one-shot drop.
static void handleDroppedFile(AppState& state, const std::string& path, bool& sawCloud)
{
auto pos = path.rfind('.');
std::string e = pos == std::string::npos ? "" : path.substr(pos + 1);
std::transform(e.begin(), e.end(), e.begin(), ::tolower);

if (e == "jpg" || e == "jpeg" || e == "png" || e == "bmp")
state.loadImage(path.c_str());
else if (e == "laz" || e == "las")
{
if (sawCloud)
state.addCloud(path.c_str());
else
{
state.loadCloud(path.c_str());
sawCloud = true;
}
}
else if (e == "yml" || e == "yaml")
state.loadIntrinsics(path.c_str());
else if (e == "json")
state.loadCalibration(path.c_str());
else
state.statusMsg = "Unsupported dropped file: " + path;
}

// ── App::update ───────────────────────────────────────────────────────────────
void App::update()
{
Expand All @@ -610,6 +642,19 @@ void App::update()
// to pick".
bool allowOrbit = !imguiWantMouse && !shiftHeld();
state.orbit.update(allowOrbit);

// Drag & drop images (*.jpg/*.jpeg/*.png/*.bmp), point clouds (*.laz/*.las), intrinsics
// (*.yml/*.yaml) or a calibration (*.json) onto the window to load them -- raylib's GLFW
// backend surfaces OS drag & drop the same way on Windows, Linux and macOS, so no
// platform-specific code is needed here.
if (IsFileDropped())
{
FilePathList dropped = LoadDroppedFiles();
bool sawCloud = false;
for (unsigned int i = 0; i < dropped.count; i++)
handleDroppedFile(state, dropped.paths[i], sawCloud);
UnloadDroppedFiles(dropped);
}
}

// ── 3D point picking + correspondence markers ───────────────────────────────
Expand Down
58 changes: 50 additions & 8 deletions apps/camera_lidar_intrinsics_calib/IntrinsicsCalib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ struct State
};

// ── helpers ───────────────────────────────────────────────────────────────────
// Reads one image file and appends it to s.images if it decodes; used by both loadDir() (a whole
// folder) and the drag & drop handler in main()'s loop below (individual dropped photos).
static bool appendImage(State& s, const std::string& path)
{
cv::Mat bgr = cv::imread(path, cv::IMREAD_COLOR);
if (bgr.empty())
return false;
CalibImage ci;
ci.path = path;
cv::cvtColor(bgr, ci.rgb, cv::COLOR_BGR2RGB);
s.images.push_back(std::move(ci));
return true;
}

static void loadDir(State& s)
{
s.images.clear();
Expand Down Expand Up @@ -121,16 +135,32 @@ static void loadDir(State& s)
std::sort(paths.begin(), paths.end());

for (auto& p : paths)
appendImage(s, p);
s.statusMsg = "Loaded " + std::to_string(s.images.size()) + " images";
}

// Drops a directory in as if Browse+Load had picked it (replaces the current image set);
// individual photo files are appended instead (accumulating shots from wherever they came from,
// since there's no single directory to re-scan for them). Used by the drag & drop handler in
// main()'s loop below.
static void handleDroppedPath(State& s, const std::string& path)
{
if (fs::is_directory(path))
{
cv::Mat bgr = cv::imread(p, cv::IMREAD_COLOR);
if (bgr.empty())
continue;
CalibImage ci;
ci.path = p;
cv::cvtColor(bgr, ci.rgb, cv::COLOR_BGR2RGB);
s.images.push_back(std::move(ci));
setBuf(s.dirBuf, sizeof(s.dirBuf), path);
loadDir(s);
return;
}

if (appendImage(s, path))
{
s.calibrated = false;
s.statusMsg = "Added " + path + " (" + std::to_string(s.images.size()) + " images total)";
}
else
{
s.statusMsg = "Unsupported dropped file: " + path;
}
s.statusMsg = "Loaded " + std::to_string(s.images.size()) + " images";
}

static void detectAll(State& s)
Expand Down Expand Up @@ -335,6 +365,18 @@ int main(int argc, char* argv[])

while (!WindowShouldClose())
{
// Drag & drop a folder of checkerboard photos (replaces the current set, like Browse+Load)
// or individual photo files (appended to the current set) onto the window to load them --
// raylib's GLFW backend surfaces OS drag & drop the same way on Windows, Linux and macOS,
// so no platform-specific code is needed here.
if (IsFileDropped())
{
FilePathList dropped = LoadDroppedFiles();
for (unsigned int i = 0; i < dropped.count; i++)
handleDroppedPath(state, dropped.paths[i]);
UnloadDroppedFiles(dropped);
}

refreshTex(state);

BeginDrawing();
Expand Down
38 changes: 38 additions & 0 deletions apps/camera_lidar_trajectory_viewer/TrajectoryViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,33 @@ static void actionOpenCalibration(AppState& s)
}
}

// Drag & drop equivalent of actionSelectLioResultDir()/actionOpenCalibration(): a dropped
// directory is this app's session (LIO result dir), and unlike the menu action it loads
// immediately instead of waiting for the "Load session" button, since a drop is already an
// explicit "load this" gesture. A dropped *.json is treated as a calibration file. Used by the
// drag & drop handler in main()'s loop below.
static void handleDroppedPath(AppState& s, const std::string& path)
{
if (fs::is_directory(path))
{
setBuf(s.sessionBuf, sizeof(s.sessionBuf), path);
loadSession(s);
return;
}

std::string ext = fs::path(path).extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if (ext == ".json")
{
setBuf(s.calibBuf, sizeof(s.calibBuf), path);
loadCalib(s);
}
else
{
s.status = "Unsupported dropped file: " + path;
}
}

static void actionExportColoredPointCloud(AppState& s)
{
std::string defaultName = fs::path(s.exportBuf).filename().string();
Expand Down Expand Up @@ -1398,6 +1425,17 @@ int main(int argc, char* argv[])
bool imguiWants = ImGui::GetIO().WantCaptureMouse;
s.orbit.updateEulerTransition(GetFrameTime());

// Drag & drop the LIO result directory (this app's session) or a calibration *.json onto
// the window to load it -- raylib's GLFW backend surfaces OS drag & drop the same way on
// Windows, Linux and macOS, so no platform-specific code is needed here.
if (IsFileDropped())
{
FilePathList dropped = LoadDroppedFiles();
if (dropped.count > 0)
handleDroppedPath(s, dropped.paths[0]);
UnloadDroppedFiles(dropped);
}

// pick up the ROS export result from the worker thread (if any)
{
std::lock_guard<std::mutex> lk(s.rosMtx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,27 @@ void loadSession(const std::string& session_file_name)
}
}

// Accepts a Mandeye JSON Session file (*.mjs/*.json) -- shared by the drag & drop handler in main()'s loop and
// the CLI argv handling below, so both accept the same input and report unsupported drops the same way.
void loadSessionFromPath(const std::string& path)
{
std::string ext = fs::path(path).extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

if (ext != ".mjs" && ext != ".json")
{
spdlog::error("Unsupported file dropped: '{}'", path);

[[maybe_unused]] pfd::message message(
"Load session", "Unsupported file:\n" + path + "\n\nDrop a session file (*.mjs/*.json).", pfd::choice::ok, pfd::icon::error);
message.result();
return;
}

session_file_name = path;
loadSession(path);
}

void openSession()
{
session_file_name = mandeye::fd::OpenFileDialogOneFile("Open session", mandeye::fd::Session_filter);
Expand Down Expand Up @@ -6043,8 +6064,7 @@ int main(int argc, char* argv[])

if (ext == ".mjs" || ext == ".json")
{
loadSession(argv[i]);

loadSessionFromPath(argv[i]);
break;
}
}
Expand Down Expand Up @@ -6081,6 +6101,20 @@ int main(int argc, char* argv[])
if (wheelMove != 0.0f)
wheel(0, wheelMove > 0.0f ? 1 : -1, mx, my);

// Drag & drop a session file (*.mjs/*.json) onto the window to load it. raylib's GLFW backend
// surfaces OS drag & drop the same way on Windows, Linux and macOS, so no platform-specific code is
// needed here. Only the first dropped path is used; loadSessionFromPath() reports unsupported drops
// via a message box instead of silently ignoring them.
if (IsFileDropped())
{
FilePathList dropped_files = LoadDroppedFiles();
if (dropped_files.count > 0)
{
loadSessionFromPath(dropped_files.paths[0]);
}
UnloadDroppedFiles(dropped_files);
}

BeginDrawing();
display();
EndDrawing();
Expand Down
Loading