1mod region;
26
27use crate::engine::attachments::{Attachment, Attachments};
28use crate::engine::diagnostic::{Diagnostic, Severity};
29use crate::engine::keyframes::{KeyframeIndex, Retrieval};
30use crate::engine::pose_graph::{PoseGraph, PoseGraphError, Write};
31use crate::kernels::features::{extract_features, Feature};
32use crate::kernels::geometry::Rect;
33use crate::kernels::raster::{downscale_gray, downscaled_size, grayscale};
34use crate::kernels::region::{Mask, Region as Shape};
35use crate::kernels::voting::{Finalized, Ring};
36use std::collections::HashMap;
37use std::fmt;
38use std::sync::Arc;
39
40pub const VOTING_RING_BYTES: usize = 24 * 1024 * 1024;
42pub const FALLBACK_FEATURES: usize = 480;
44pub const PATCH_COUNT: usize = 24;
46pub const PATCH_SIZE: usize = 32;
47pub const POSE_BOUND: f64 = crate::engine::compositor::CANVAS_PIXEL_BOUND - 65536.0;
49
50pub fn is_valid_pose(x: f64, y: f64) -> bool {
52 x.is_finite() && y.is_finite() && x.abs() < POSE_BOUND && y.abs() < POSE_BOUND
53}
54
55pub struct SolveRegion {
57 pub id: String,
58 pub name: String,
59 pub moving: bool,
61 pub kind: String,
63 pub unassigned: bool,
64 pub code: u8,
66 pub shape: Shape,
67}
68
69pub struct SolveConfig {
70 pub width: usize,
71 pub height: usize,
72 pub factor: u32,
74 pub radius: i32,
76 pub noise: f64,
78 pub voting_budget: usize,
79 pub regions: Vec<SolveRegion>,
80}
81
82#[derive(Clone, Copy, Debug, PartialEq)]
84pub struct ScanFields {
85 pub duplicate: bool,
86 pub zoom: f64,
87 pub difference: f64,
88}
89
90#[derive(Clone, Debug, PartialEq)]
92pub struct Plane {
93 pub width: usize,
94 pub height: usize,
95 pub data: Vec<u8>,
96}
97
98#[derive(Clone, Debug, PartialEq)]
100pub struct KeyPatch {
101 pub x: f64,
102 pub y: f64,
103 pub size: usize,
104 pub data: Vec<u8>,
105}
106
107#[derive(Clone, Debug, PartialEq)]
109pub struct Keyframe {
110 pub id: String,
111 pub node: String,
112 pub canvas_id: String,
113 pub layer: String,
114 pub frame: u32,
115 pub features: Vec<Feature>,
116 pub gray: Arc<Plane>,
117 pub x: f64,
118 pub y: f64,
119 pub scale_x: f64,
120 pub scale_y: f64,
121 pub patches: Vec<KeyPatch>,
122}
123
124#[derive(Clone, Debug, PartialEq)]
126pub struct CanvasMeta {
127 pub id: String,
128 pub layer: String,
129 pub name: String,
130 pub kind: String,
131 pub fragment: u32,
132 pub first_time: f64,
133 pub last_time: f64,
134 pub attached_to: Option<String>,
135}
136
137#[derive(Clone, Debug, PartialEq)]
139pub struct Placement {
140 pub layer: String,
141 pub canvas_id: String,
142 pub node: String,
143 pub x: f64,
144 pub y: f64,
145 pub confidence: f64,
146 pub uncertain: bool,
147 pub time: f64,
148 pub skip: bool,
149 pub occlusions: Vec<Rect>,
150}
151
152pub type FragmentScale = Option<(f64, f64)>;
154
155#[derive(Clone, Debug, PartialEq)]
157pub enum Effect {
158 Canvas {
160 meta: CanvasMeta,
161 created: bool,
162 },
163 Attachment(Attachment),
165 Keyframe(Arc<Keyframe>),
167 Graph(Write),
169 Diagnostic(Diagnostic),
170}
171
172#[derive(Clone, Debug, PartialEq)]
174pub struct VotingVerdict {
175 pub region: String,
176 pub x0: i32,
177 pub y0: i32,
178 pub w: i32,
179 pub h: i32,
180 pub bits: Vec<u8>,
181 pub clean: Vec<u8>,
182}
183
184#[derive(Clone, Debug, PartialEq)]
186pub struct VotingRecord {
187 pub index: u32,
188 pub verdicts: Vec<VotingVerdict>,
189 pub voted_layers: u32,
190 pub thin_layers: u32,
191}
192
193#[derive(Clone, Debug, PartialEq)]
195pub struct FrameDone {
196 pub placements: Vec<Placement>,
198 pub joined_ring: bool,
201 pub voting: Vec<VotingRecord>,
202}
203
204#[derive(Clone, Debug, PartialEq)]
206pub enum Begin {
207 Duplicate(Vec<Placement>),
209 NeedFrame { gray: bool },
212}
213
214#[derive(Clone, Debug, PartialEq)]
215pub enum Poll {
216 NeedKeyframes(Vec<String>),
218 Done(FrameDone),
219}
220
221#[derive(Clone, Debug, PartialEq)]
222pub enum SolveError {
223 Graph(PoseGraphError),
224 BadInput(&'static str),
226}
227
228impl fmt::Display for SolveError {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self {
231 SolveError::Graph(e) => e.fmt(f),
232 SolveError::BadInput(what) => write!(f, "CORE_BAD_ARGUMENT: {what}."),
233 }
234 }
235}
236
237impl std::error::Error for SolveError {}
238
239impl From<PoseGraphError> for SolveError {
240 fn from(e: PoseGraphError) -> Self {
241 SolveError::Graph(e)
242 }
243}
244
245struct RegionState {
247 canvas_id: String,
248 fragment: u32,
249 pose: (f64, f64),
250 velocity: (f64, f64),
252 last_node: Option<(String, u32)>,
254 anchor: Option<Arc<Keyframe>>,
255 previous_features: Option<Vec<Feature>>,
256 blind: bool,
258 weak: bool,
260 started: bool,
262}
263
264struct FrameWork {
266 index: u32,
267 time: f64,
268 scan: ScanFields,
269 gray: Arc<Plane>,
270 features: Arc<Vec<Feature>>,
271 placements: Vec<Placement>,
272 region: usize,
274 step: Option<Box<region::Step>>,
275 joined_ring: bool,
276}
277
278enum Stage {
279 Idle,
280 AwaitFrame {
281 index: u32,
282 time: f64,
283 scan: ScanFields,
284 },
285 Frame(Box<FrameWork>),
286}
287
288pub struct SolveSession {
289 width: usize,
290 height: usize,
291 factor: u32,
292 radius: i32,
293 analysis: (usize, usize),
294 regions: Vec<SolveRegion>,
295 states: Vec<RegionState>,
296 voting_slot: Vec<Option<usize>>,
298 voting: Ring,
299 canvas_slots: HashMap<String, u32>,
300 graph: PoseGraph,
301 index: KeyframeIndex,
302 attachments: Attachments,
303 canvases: HashMap<String, CanvasMeta>,
304 frames: [Vec<u8>; 2],
305 previous_slot: Option<usize>,
307 current_slot: usize,
308 native: Vec<u8>,
309 native_filled: bool,
310 previous_gray: Option<Arc<Plane>>,
311 previous_features: Option<Arc<Vec<Feature>>>,
312 previous_plan: Option<Vec<Placement>>,
313 stage: Stage,
314 effects: Vec<Effect>,
315 #[cfg(test)]
317 seams: tests::Seams,
318}
319
320fn clone_shape(s: &Shape) -> Shape {
321 Shape {
322 rect: s.rect,
323 exclusions: s.exclusions.clone(),
324 crop: s.crop,
325 solid: s.solid,
326 mask: s.mask.as_ref().map(|m| Mask {
327 width: m.width,
328 height: m.height,
329 factor: m.factor,
330 data: m.data.clone(),
331 }),
332 }
333}
334
335fn stored(mut f: Feature) -> Feature {
337 f.score = f.score as f32 as f64;
338 f
339}
340
341impl SolveSession {
342 pub fn new(config: SolveConfig) -> SolveSession {
343 let SolveConfig {
344 width,
345 height,
346 factor,
347 radius,
348 noise,
349 voting_budget,
350 regions,
351 } = config;
352 let factor = factor.max(1);
353 let analysis = downscaled_size(width, height, factor as usize);
354 let mut voting_slot = Vec::with_capacity(regions.len());
355 let mut voting_regions = Vec::new();
356 for r in ®ions {
357 if r.moving {
358 voting_slot.push(Some(voting_regions.len()));
359 voting_regions.push(clone_shape(&r.shape));
360 } else {
361 voting_slot.push(None);
362 }
363 }
364 let voting = Ring::new(
365 factor as i32,
366 noise,
367 width,
368 height,
369 analysis.0,
370 analysis.1,
371 voting_budget,
372 voting_regions,
373 );
374 let states = regions
375 .iter()
376 .map(|_| RegionState {
377 canvas_id: String::new(),
378 fragment: 0,
379 pose: (0.0, 0.0),
380 velocity: (0.0, 0.0),
381 last_node: None,
382 anchor: None,
383 previous_features: None,
384 blind: false,
385 weak: false,
386 started: false,
387 })
388 .collect();
389 SolveSession {
390 width,
391 height,
392 factor,
393 radius,
394 analysis,
395 regions,
396 states,
397 voting_slot,
398 voting,
399 canvas_slots: HashMap::new(),
400 graph: PoseGraph::new(),
401 index: KeyframeIndex::new(),
402 attachments: Attachments::new(),
403 canvases: HashMap::new(),
404 frames: [vec![0; width * height * 4], vec![0; width * height * 4]],
405 previous_slot: None,
406 current_slot: 0,
407 native: vec![0; width * height],
408 native_filled: false,
409 previous_gray: None,
410 previous_features: None,
411 previous_plan: None,
412 stage: Stage::Idle,
413 effects: Vec::new(),
414 #[cfg(test)]
415 seams: tests::Seams::default(),
416 }
417 }
418
419 pub fn width(&self) -> usize {
420 self.width
421 }
422
423 pub fn height(&self) -> usize {
424 self.height
425 }
426
427 pub fn analysis_size(&self) -> (usize, usize) {
429 self.analysis
430 }
431
432 pub fn graph(&self) -> &PoseGraph {
434 &self.graph
435 }
436
437 pub fn graph_mut(&mut self) -> &mut PoseGraph {
438 &mut self.graph
439 }
440
441 pub fn take_graph(&mut self) -> PoseGraph {
443 std::mem::take(&mut self.graph)
444 }
445
446 pub fn take_effects(&mut self) -> Vec<Effect> {
448 std::mem::take(&mut self.effects)
449 }
450
451 pub fn begin_frame(
453 &mut self,
454 index: u32,
455 time: f64,
456 scan: ScanFields,
457 ) -> Result<Begin, SolveError> {
458 if !matches!(self.stage, Stage::Idle) {
459 return Err(SolveError::BadInput("a frame is already in progress"));
460 }
461 if scan.duplicate && self.states.iter().all(|s| !s.blind) {
462 if let Some(previous) = &mut self.previous_plan {
463 for p in previous.iter_mut() {
464 p.time = time;
465 }
466 let placements = previous.clone();
467 for s in &mut self.states {
468 s.velocity = (0.0, 0.0);
469 }
470 return Ok(Begin::Duplicate(placements));
471 }
472 }
473 self.current_slot = match self.previous_slot {
474 Some(p) => 1 - p,
475 None => 0,
476 };
477 self.stage = Stage::AwaitFrame { index, time, scan };
478 Ok(Begin::NeedFrame {
479 gray: !(scan.duplicate && self.previous_gray.is_some()),
480 })
481 }
482
483 pub fn frame_mut(&mut self) -> &mut [u8] {
485 &mut self.frames[self.current_slot]
486 }
487
488 pub fn run_frame(
492 &mut self,
493 gray: Option<Vec<u8>>,
494 features: Option<Vec<Feature>>,
495 labels: &[u8],
496 ) -> Result<Poll, SolveError> {
497 let Stage::AwaitFrame { index, time, scan } =
498 std::mem::replace(&mut self.stage, Stage::Idle)
499 else {
500 return Err(SolveError::BadInput("no frame was asked for"));
501 };
502 if labels.len() != self.width * self.height {
503 return Err(SolveError::BadInput(
504 "the label plane does not match the frame",
505 ));
506 }
507 let (aw, ah) = self.analysis;
508 let gray = match (scan.duplicate, &self.previous_gray, gray) {
509 (true, Some(previous), _) => previous.clone(),
510 (_, _, Some(data)) => {
511 if data.len() != aw * ah {
512 return Err(SolveError::BadInput(
513 "analysis gray size differs from the run",
514 ));
515 }
516 Arc::new(Plane {
517 width: aw,
518 height: ah,
519 data,
520 })
521 }
522 _ => {
523 let mut data = vec![0; aw * ah];
524 downscale_gray(
525 &self.frames[self.current_slot],
526 self.width,
527 self.height,
528 self.factor as usize,
529 &mut data,
530 );
531 Arc::new(Plane {
532 width: aw,
533 height: ah,
534 data,
535 })
536 }
537 };
538 let features = match features {
539 Some(f) if !scan.duplicate => Arc::new(f.into_iter().map(stored).collect()),
540 _ => match (scan.duplicate, &self.previous_features) {
541 (true, Some(previous)) => previous.clone(),
542 _ => Arc::new(
543 extract_features(&gray.data, aw, ah, FALLBACK_FEATURES, None)
544 .into_iter()
545 .map(stored)
546 .collect(),
547 ),
548 },
549 };
550 self.native_filled = false;
551 self.stage = Stage::Frame(Box::new(FrameWork {
552 index,
553 time,
554 scan,
555 gray,
556 features,
557 placements: Vec::with_capacity(self.regions.len()),
558 region: 0,
559 step: None,
560 joined_ring: false,
561 }));
562 self.advance(labels, None)
563 }
564
565 pub fn provide_keyframes(
567 &mut self,
568 rows: Vec<Option<Keyframe>>,
569 labels: &[u8],
570 ) -> Result<Poll, SolveError> {
571 let waiting = match &self.stage {
572 Stage::Frame(work) => work.step.as_ref().is_some_and(|s| s.waiting()),
573 _ => false,
574 };
575 if !waiting {
576 return Err(SolveError::BadInput("no keyframes were asked for"));
577 }
578 self.advance(labels, Some(rows))
579 }
580
581 fn advance(
583 &mut self,
584 labels: &[u8],
585 mut rows: Option<Vec<Option<Keyframe>>>,
586 ) -> Result<Poll, SolveError> {
587 loop {
588 let Stage::Frame(work) = &mut self.stage else {
589 return Err(SolveError::BadInput("no frame in progress"));
590 };
591 if work.region == self.regions.len() {
592 return Ok(Poll::Done(self.finish_frame()));
593 }
594 let i = work.region;
595 let step = work.step.take();
596 let ctx = region::FrameCtx {
597 index: work.index,
598 time: work.time,
599 scan: work.scan,
600 gray: work.gray.clone(),
601 features: work.features.clone(),
602 };
603 let result = match step {
604 None => self.step_region(i, &ctx, labels),
605 Some(step) => self.resume_region(i, &ctx, *step, rows.take().unwrap_or_default()),
606 };
607 let Stage::Frame(work) = &mut self.stage else {
608 unreachable!()
609 };
610 match result {
611 Ok(region::Outcome::Placed(placement, observed)) => {
612 work.joined_ring |= observed;
613 work.placements.push(placement);
614 work.region += 1;
615 }
616 Ok(region::Outcome::Wait(step, ids)) => {
617 work.step = Some(step);
618 return Ok(Poll::NeedKeyframes(ids));
619 }
620 Err(e) => {
621 self.stage = Stage::Idle;
622 return Err(e);
623 }
624 }
625 }
626 }
627
628 fn finish_frame(&mut self) -> FrameDone {
630 let Stage::Frame(work) = std::mem::replace(&mut self.stage, Stage::Idle) else {
631 unreachable!()
632 };
633 let FrameWork {
634 index,
635 gray,
636 features,
637 placements,
638 joined_ring,
639 ..
640 } = *work;
641 let voting = if joined_ring {
642 let finalized = self.voting.push_frame(index);
643 self.records(finalized)
644 } else {
645 Vec::new()
646 };
647 self.previous_plan = Some(placements.clone());
648 self.previous_features = Some(features);
649 self.previous_slot = Some(self.current_slot);
650 self.previous_gray = Some(gray);
651 FrameDone {
652 placements,
653 joined_ring,
654 voting,
655 }
656 }
657
658 fn records(&self, finalized: Vec<Finalized>) -> Vec<VotingRecord> {
659 let moving: Vec<&SolveRegion> = self.regions.iter().filter(|r| r.moving).collect();
660 finalized
661 .into_iter()
662 .map(|f| VotingRecord {
663 index: f.index,
664 verdicts: f
665 .verdicts
666 .into_iter()
667 .map(|v| {
668 let b = self.voting.slots[v.slot].box_;
669 VotingVerdict {
670 region: moving[v.slot].id.clone(),
671 x0: b.x0,
672 y0: b.y0,
673 w: b.w,
674 h: b.h,
675 bits: v.bits,
676 clean: v.clean,
677 }
678 })
679 .collect(),
680 voted_layers: f.voted_layers,
681 thin_layers: f.thin_layers,
682 })
683 .collect()
684 }
685
686 pub fn drain_voting(&mut self) -> Vec<VotingRecord> {
688 let finalized = self.voting.drain();
689 self.records(finalized)
690 }
691
692 fn native(&mut self) -> &[u8] {
694 if !self.native_filled {
695 grayscale(&self.frames[self.current_slot], &mut self.native);
696 self.native_filled = true;
697 }
698 &self.native
699 }
700
701 fn canvas_slot(&mut self, canvas_id: &str) -> u32 {
702 let next = self.canvas_slots.len() as u32;
703 *self
704 .canvas_slots
705 .entry(canvas_id.to_string())
706 .or_insert(next)
707 }
708
709 fn new_canvas(&mut self, i: usize, time: f64) {
711 let region = &self.regions[i];
712 let state = &mut self.states[i];
713 state.canvas_id = format!("{}-part-{}", region.id, state.fragment);
714 let name = if state.fragment > 0 {
715 format!("{} · 未定位片段 {}", region.name, state.fragment)
716 } else {
717 region.name.clone()
718 };
719 let meta = CanvasMeta {
720 id: state.canvas_id.clone(),
721 layer: region.id.clone(),
722 name,
723 kind: region.kind.clone(),
724 fragment: state.fragment,
725 first_time: time,
726 last_time: time,
727 attached_to: None,
728 };
729 self.canvases.insert(meta.id.clone(), meta.clone());
730 self.effects.push(Effect::Canvas {
731 meta,
732 created: true,
733 });
734 }
735
736 fn diagnostic(&mut self, d: Diagnostic) {
737 self.effects.push(Effect::Diagnostic(d));
738 }
739
740 fn graph_writes(&mut self, writes: Vec<Write>) {
741 self.effects.extend(writes.into_iter().map(Effect::Graph));
742 }
743
744 fn retrieve(
746 &mut self,
747 i: usize,
748 frame: u32,
749 own: &[Feature],
750 exclude: Option<&str>,
751 ) -> Retrieval {
752 let descriptors: Vec<_> = own.iter().map(|f| f.descriptor).collect();
753 let retrieval =
754 self.index
755 .retrieve(&self.regions[i].id, frame, &descriptors, exclude, None);
756 if retrieval.budget_warning {
757 self.diagnostic(Diagnostic::new(
758 crate::engine::keyframes::BUDGET_DIAGNOSTIC,
759 Severity::Warning,
760 ));
761 }
762 retrieval
763 }
764}
765
766#[cfg(test)]
767mod tests;