Skip to main content

hooks/
hooks.rs

1/*
2 * hooks.rs
3 *
4 * Copyright (c) 2026 ParkSangWoo
5 *
6 * Author(s):
7 *
8 *      ParkSangWoo <dev.parksangwoo@gmail.com>
9 *
10 *
11 * This file is part of cumulus3d.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 *      http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 *
25 * SPDX-License-Identifier: Apache-2.0
26 */
27
28//! Closure hook example: push a synthetic scene (3 drones x 8 positions) in position by position, print the events and the
29//! preview/refined point counts, and use the default output hook to produce the same files as `cumulus3d stream` (timeline.txt, run.log,
30//! snapshots/ ...).
31//!
32//! ```bash
33//! cargo run --release -p cumulus3d-cli --example hooks -- [output folder]
34//! ```
35//! With a CUDA device it densifies as well; without one it runs with no densification (point cloud 0, sparse point count only).
36
37#[path = "../tests/common/mod.rs"]
38mod common;
39
40use cumulus3d_cli::densewrap::{make_pm_backend, parse_profile, DenseConfig};
41use cumulus3d_cli::events::{Event, EventKind};
42use cumulus3d_cli::pipeline::Pipeline;
43use cumulus3d_cli::session::{FrameSet, Input, Session, SessionConfig};
44use cumulus3d_cli::sinks::{self, SinkOptions};
45use cumulus3d_cli::stream::{Layout, CAMS};
46use cumulus3d_core::io::read_gps_file;
47use std::path::PathBuf;
48
49fn main() -> Result<(), String> {
50    let out = std::env::args().nth(1).map(PathBuf::from).unwrap_or_else(|| std::env::temp_dir().join("cumulus3d_hooks"));
51    let src = out.with_extension("src");
52    if !src.join("gps_ref.txt").exists() {
53        common::make_dataset(&src);
54    }
55    if out.exists() {
56        std::fs::remove_dir_all(&out).map_err(|e| e.to_string())?;
57    }
58    let layout = Layout::discover(&src, 1)?;
59    let npos = layout.frames.len();
60
61    let mut cfg = SessionConfig::new(src.join("images"));
62    cfg.gps = read_gps_file(src.join("gps_ref.txt")).map_err(|e| e.to_string())?;
63    (cfg.span, cfg.overlap, cfg.total_positions, cfg.seed) = (4, 1, Some(npos), Some(7));
64    if cumulus3d_cuda::is_available() {
65        let mut d = DenseConfig::new(make_pm_backend("cuda"), parse_profile("fast")?);
66        d.undistort.max_image_size = 160;
67        cfg.dense = Some(d);
68    }
69
70    // Hook: print event text, zone point-cloud point counts, warnings and errors; plus the default output hook (files).
71    let pipeline = Pipeline::new(Session::new(cfg))
72        .on_any(|e: &Event| {
73            if let Some(t) = e.timeline_text() {
74                println!("[event v{}] {t}", e.meta().version);
75            }
76        })
77        .on(EventKind::ZonePreview, |e: &Event| {
78            if let Event::ZonePreview { range, cloud, model, .. } = e {
79                println!("  초벌 {}: 조밀 점 {} / 희소 점 {}", range.zone, cloud.len(), model.num_points3d());
80            }
81        })
82        .on(EventKind::ZoneRefined, |e: &Event| {
83            if let Event::ZoneRefined { range, cloud, model, .. } = e {
84                println!("  정밀 {}: 조밀 점 {} / 희소 점 {}", range.zone, cloud.len(), model.num_points3d());
85            }
86        })
87        .on_message(|k, m| eprintln!("  {k:?}: {m}"));
88    let mut pipeline = sinks::attach(pipeline, &out, &SinkOptions::default()).map_err(|e| e.to_string())?;
89
90    for p in 0..npos {
91        pipeline.push(Input::Frames(FrameSet::new(CAMS.iter().map(|c| (*c, layout.name(c, p))))));
92    }
93    let (_, summary) = pipeline.finish();
94    println!("이벤트 {} 개, 훅 호출 {} 번, 훅 패닉 {}", summary.events, summary.hook_calls, summary.hook_panics);
95
96    for f in ["timeline.txt", "run.log", "snapshots/manifest.json", "snapshots/timeline.txt", "DONE"] {
97        let p = out.join(f);
98        println!("{} {}", if p.exists() { "있음" } else { "없음" }, p.display());
99    }
100    Ok(())
101}