use super::f26dot6::{F2Dot14, F26Dot6};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RoundState {
HalfGrid,
Grid,
DoubleGrid,
DownToGrid,
UpToGrid,
Off,
Super,
Super45,
}
#[derive(Clone, Debug)]
pub struct GraphicsState {
pub freedom_vector: (F2Dot14, F2Dot14),
pub projection_vector: (F2Dot14, F2Dot14),
pub dual_projection_vector: (F2Dot14, F2Dot14),
pub rp0: u32,
pub rp1: u32,
pub rp2: u32,
pub zp0: u32,
pub zp1: u32,
pub zp2: u32,
pub loop_value: u32,
pub round_state: RoundState,
pub minimum_distance: F26Dot6,
pub control_value_cut_in: F26Dot6,
pub single_width_cut_in: F26Dot6,
pub single_width_value: F26Dot6,
pub auto_flip: bool,
pub delta_base: u16,
pub delta_shift: u16,
pub instruct_control: u8,
pub scan_control: u32,
pub scan_type: i32,
pub super_round_period: F26Dot6,
pub super_round_phase: F26Dot6,
pub super_round_threshold: F26Dot6,
}
impl Default for GraphicsState {
fn default() -> Self {
GraphicsState {
freedom_vector: (F2Dot14::ONE, F2Dot14::ZERO),
projection_vector: (F2Dot14::ONE, F2Dot14::ZERO),
dual_projection_vector: (F2Dot14::ONE, F2Dot14::ZERO),
rp0: 0,
rp1: 0,
rp2: 0,
zp0: 1, zp1: 1,
zp2: 1,
loop_value: 1,
round_state: RoundState::Grid,
minimum_distance: F26Dot6::ONE, control_value_cut_in: F26Dot6(68), single_width_cut_in: F26Dot6::ZERO,
single_width_value: F26Dot6::ZERO,
auto_flip: true,
delta_base: 9,
delta_shift: 3,
instruct_control: 0,
scan_control: 0,
scan_type: 0,
super_round_period: F26Dot6(64), super_round_phase: F26Dot6::ZERO,
super_round_threshold: F26Dot6(32), }
}
}
impl GraphicsState {
pub fn round(&self, distance: F26Dot6) -> F26Dot6 {
let sign = if distance.0 >= 0 { 1i32 } else { -1i32 };
let val = distance.abs();
let result = match self.round_state {
RoundState::Off => return distance,
RoundState::Grid => val.round(),
RoundState::HalfGrid => {
let floored = val.floor();
F26Dot6(floored.0 + 32)
}
RoundState::DoubleGrid => {
F26Dot6((val.0 + 16) & !31)
}
RoundState::DownToGrid => val.floor(),
RoundState::UpToGrid => {
if val.0 & 63 == 0 {
val
} else {
val.ceil()
}
}
RoundState::Super | RoundState::Super45 => {
self.super_round(val)
}
};
let result = if result.0 < 0 { F26Dot6::ZERO } else { result };
F26Dot6(result.0 * sign)
}
fn super_round(&self, val: F26Dot6) -> F26Dot6 {
let period = self.super_round_period;
let phase = self.super_round_phase;
let threshold = self.super_round_threshold;
if period.0 == 0 {
return val;
}
let val_minus_phase = F26Dot6(val.0 - phase.0);
let rounded = if val_minus_phase.0 >= 0 {
let n = (val_minus_phase.0 + threshold.0) / period.0;
F26Dot6(n * period.0 + phase.0)
} else {
let n = -((-val_minus_phase.0 + threshold.0) / period.0);
F26Dot6(n * period.0 + phase.0)
};
if rounded.0 < phase.0 {
F26Dot6(phase.0)
} else {
rounded
}
}
pub fn set_super_round(&mut self, n: u32, is_45: bool) {
let period_bits = (n >> 6) & 0x03;
self.super_round_period = match period_bits {
0 => F26Dot6(32), 1 => F26Dot6(64), 2 => F26Dot6(128), _ => F26Dot6(64), };
if is_45 {
self.super_round_period = F26Dot6(
(self.super_round_period.0 as i64 * 46 / 64) as i32,
);
}
let phase_bits = (n >> 4) & 0x03;
self.super_round_phase = match phase_bits {
0 => F26Dot6::ZERO,
1 => F26Dot6(16), 2 => F26Dot6(32), 3 => F26Dot6(48), _ => unreachable!(),
};
let threshold_bits = n & 0x0F;
self.super_round_threshold = if threshold_bits == 0 {
F26Dot6(self.super_round_period.0 - 1)
} else {
F26Dot6((threshold_bits as i32 - 4) * self.super_round_period.0 / 8)
};
}
}