use crate::{num::CheapOrderedFloat, SegIdx};
use super::{ChangedInterval, OutputEvent, SweepLine};
#[derive(Clone, Debug, PartialEq)]
struct HFrag {
pub seg: SegIdx,
pub start: f64,
pub end: f64,
pub connected_at_start: bool,
pub connected_at_end: bool,
pub enter_first: bool,
pub sweep_idx: Option<usize>,
pub old_sweep_idx: Option<usize>,
}
impl HFrag {
pub fn from_position(pos: OutputEvent) -> Option<Self> {
let OutputEvent {
x0,
connected_above,
x1,
connected_below,
..
} = pos;
if x0 == x1 {
return None;
}
let enter_first = x0 < x1;
let (start, end, connected_at_start, connected_at_end) = if enter_first {
(x0, x1, connected_above, connected_below)
} else {
(x1, x0, connected_below, connected_above)
};
Some(HFrag {
end,
start,
enter_first,
seg: pos.seg_idx,
connected_at_start,
connected_at_end,
sweep_idx: pos.sweep_idx,
old_sweep_idx: pos.old_sweep_idx,
})
}
pub fn connected_above_at(&self, x: f64) -> bool {
(x == self.start && self.enter_first && self.connected_at_start)
|| (x == self.end && !self.enter_first && self.connected_at_end)
}
pub fn connected_below_at(&self, x: f64) -> bool {
(x == self.start && !self.enter_first && self.connected_at_start)
|| (x == self.end && self.enter_first && self.connected_at_end)
}
}
#[derive(Debug)]
pub struct SweepLineRange<'bufs, 'state, 'segs> {
last_x: Option<f64>,
line: &'state SweepLine<'state, 'state, 'segs>,
bufs: &'bufs mut SweepLineRangeBuffers,
changed_interval: ChangedInterval,
output_events: &'state [OutputEvent],
}
impl<'bufs, 'state, 'segs> SweepLineRange<'bufs, 'state, 'segs> {
pub(crate) fn new(
line: &'state SweepLine<'state, 'state, 'segs>,
output_events: &'state [OutputEvent],
bufs: &'bufs mut SweepLineRangeBuffers,
changed_interval: ChangedInterval,
) -> Self {
let ret = Self {
last_x: None,
output_events,
changed_interval,
line,
bufs,
};
#[cfg(feature = "slow-asserts")]
ret.check_invariants();
ret
}
fn output_events(&self) -> &[OutputEvent] {
self.output_events
}
pub fn x(&self) -> Option<f64> {
match (
self.bufs.active_horizontals.first(),
self.output_events().first(),
) {
(None, None) => None,
(None, Some(pos)) => Some(pos.smaller_x()),
(Some(h), None) => Some(h.end),
(Some(h), Some(pos)) => Some((h.end).min(pos.smaller_x())),
}
}
fn positions_at_x<'c, 'b: 'c>(&'b self, x: f64) -> impl Iterator<Item = &'b OutputEvent> + 'c {
self.output_events()
.iter()
.take_while(move |p| p.smaller_x() == x)
}
pub fn update_segments_at_x(&self, segs: &mut SegmentsConnectedAtX) {
segs.connected_up.clear();
segs.connected_down.clear();
let Some(x) = self.x() else {
return;
};
for hseg in &self.bufs.active_horizontals {
if hseg.connected_above_at(x) {
segs.connected_up
.push((hseg.seg, hseg.old_sweep_idx.unwrap()));
}
if hseg.connected_below_at(x) {
segs.connected_down
.push((hseg.seg, hseg.sweep_idx.unwrap()));
}
}
for pos in self.positions_at_x(x) {
if pos.connected_above_at(x) {
segs.connected_up
.push((pos.seg_idx, pos.old_sweep_idx.unwrap()));
}
if pos.connected_below_at(x) {
segs.connected_down
.push((pos.seg_idx, pos.sweep_idx.unwrap()));
}
}
segs.connected_up
.sort_by_key(|(_seg_idx, sweep_idx)| *sweep_idx);
segs.connected_down
.sort_by_key(|(_seg_idx, sweep_idx)| *sweep_idx);
}
pub fn active_horizontals(&self) -> impl Iterator<Item = SegIdx> + '_ {
self.bufs.active_horizontals.iter().map(|hseg| hseg.seg)
}
pub fn active_horizontals_and_orientations(&self) -> impl Iterator<Item = (SegIdx, bool)> + '_ {
self.bufs
.active_horizontals
.iter()
.map(|hseg| (hseg.seg, hseg.enter_first))
}
pub fn events(&mut self) -> Option<Vec<OutputEvent>> {
let next_x = self.x()?;
let mut ret = Vec::new();
for h in &self.bufs.active_horizontals {
let x0 = self.last_x.unwrap();
let x1 = next_x.min(h.end);
let connected_end = x1 == h.end && h.connected_at_end;
let connected_start = x0 == h.start && h.connected_at_start;
if h.enter_first {
ret.push(OutputEvent::new(
h.seg,
x0,
connected_start,
x1,
connected_end,
h.sweep_idx,
h.old_sweep_idx,
));
} else {
ret.push(OutputEvent::new(
h.seg,
x1,
connected_end,
x0,
connected_start,
h.sweep_idx,
h.old_sweep_idx,
));
}
}
self.drain_active_horizontals(next_x);
while let Some(ev) = self.output_events.first() {
if ev.smaller_x() > next_x {
break;
}
self.output_events = &self.output_events[1..];
if ev.x0 == ev.x1 {
ret.push(ev.clone());
} else if let Some(hseg) = HFrag::from_position(ev.clone()) {
self.bufs.active_horizontals.push(hseg);
}
}
self.bufs
.active_horizontals
.sort_by_key(|a| CheapOrderedFloat::from(a.end));
self.last_x = Some(next_x);
Some(ret)
}
pub fn increase_x(&mut self) {
if let Some(x) = self.x() {
self.drain_active_horizontals(x);
while let Some(ev) = self.output_events.first() {
if ev.smaller_x() > x {
break;
}
self.output_events = &self.output_events[1..];
if let Some(hseg) = HFrag::from_position(ev.clone()) {
self.bufs.active_horizontals.push(hseg);
}
}
}
self.bufs
.active_horizontals
.sort_by_key(|a| CheapOrderedFloat::from(a.end));
}
fn drain_active_horizontals(&mut self, x: f64) {
let new_start = self
.bufs
.active_horizontals
.iter()
.position(|h| h.end > x)
.unwrap_or(self.bufs.active_horizontals.len());
self.bufs.active_horizontals.drain(..new_start);
}
pub fn seg_range(&self) -> ChangedInterval {
self.changed_interval.clone()
}
pub fn old_segment_range(&self) -> impl Iterator<Item = SegIdx> + '_ {
let range = self.changed_interval.segs.clone();
self.line().old_segment_range(range)
}
pub fn segment_range(&self) -> impl Iterator<Item = SegIdx> + '_ {
let range = self.changed_interval.segs.clone();
self.line().segment_range(range)
}
pub fn line(&self) -> &SweepLine<'_, '_, 'segs> {
self.line
}
#[cfg(feature = "slow-asserts")]
fn check_invariants(&self) {
for ev in self.output_events() {
let seg = &self.line.segments()[ev.seg_idx];
let y = self.line.y();
let eps = self.line.eps();
let (lower, upper) = if seg.is_horizontal() {
(seg.start().x - 2.0 * eps, seg.end().x + 2.0 * eps)
} else {
(seg.lower(y, 2.0 * eps), seg.upper(y, 2.0 * eps))
};
assert!(ev.smaller_x() <= upper && lower <= ev.larger_x());
}
}
}
#[derive(Debug, Default)]
pub struct SegmentsConnectedAtX {
connected_up: Vec<(SegIdx, usize)>,
connected_down: Vec<(SegIdx, usize)>,
}
impl SegmentsConnectedAtX {
pub fn connected_up(&self) -> impl Iterator<Item = SegIdx> + '_ {
self.connected_up.iter().map(|x| x.0)
}
pub fn connected_down(&self) -> impl Iterator<Item = SegIdx> + '_ {
self.connected_down.iter().map(|x| x.0)
}
}
#[derive(Clone, Debug, Default)]
pub struct SweepLineRangeBuffers {
active_horizontals: Vec<HFrag>,
}
impl SweepLineRangeBuffers {
pub(crate) fn clear(&mut self) {
self.active_horizontals.clear();
}
}