use crate::{PX_SHIFT, PixelRegion, Px, PxVec2, REL_SHIFT, UiRegion, UiSpan}; use super::Len; /// How many bits of a box a [`WideLen`] keeps, and how many of a pixel. Both /// are the grid's own shift plus the room an `i64` has left over: a `Rel` is /// a fraction of a box and needs eight bits above the point, a `Px` counts up /// to two million of them and needs twenty-two. const WIDE_REL: u32 = 48; const WIDE_PX: u32 = 32; const REL_GAIN: u32 = WIDE_REL - REL_SHIFT; const PX_GAIN: u32 = WIDE_PX - PX_SHIFT; /// A length part-way through a composition, on a finer grid than the [`Len`] /// it came from and will go back to. /// /// Composing a box through its ancestors is four multiplies a level, and in /// `Len` every one of them lands back on the grid before the next starts, so /// the error grows with the depth of the tree. Stepping through this instead /// rounds once, at the end -- which matters because the same box is reached /// two ways, composed down the chain and measured against the window, and a /// structural layout decision turns on the two being one number. /// /// Twenty-four extra bits of a box and twenty-two of a pixel are far more /// than a chain can spend: the residue is `2^-48` of a box a level, against /// `2^-24` before. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct WideLen { /// Counts `1/2^WIDE_REL` of the box this length is a part of. rel: i64, /// Counts `1/2^WIDE_PX` of a pixel. px: i64, } impl WideLen { pub const fn of(len: Len) -> Self { Self { rel: (len.rel.raw() as i64) << REL_GAIN, px: (len.px.raw() as i64) << PX_GAIN, } } /// This length composed through the box it sits in, which is the same /// expression [`Len::within`] uses with the rounding left out. The parent /// is a stored box and so is on the ordinary grid; only the part being /// carried needs the room. pub const fn within(self, parent: &UiSpan) -> Self { let rel_span = (parent.end.rel.raw() - parent.start.rel.raw()) as i128; let px_span = (parent.end.px.raw() - parent.start.px.raw()) as i128; let rel = ((parent.start.rel.raw() as i64) << REL_GAIN) + ((rel_span * self.rel as i128) >> REL_SHIFT) as i64; // A pixel span of the parent, taken at this length's fraction of it: // the product is `WIDE_REL + PX_SHIFT` bits under the point and the // answer is `WIDE_PX`, so what comes off is the difference. let px = self.px + ((parent.start.px.raw() as i64) << PX_GAIN) + ((px_span * self.rel as i128) >> (WIDE_REL + PX_SHIFT - WIDE_PX)) as i64; Self { rel, px } } /// Back onto the grid against a box of `len` pixels, which is the one /// rounding a whole composition gets. Both parts are put over /// `WIDE_REL + PX_SHIFT` first so that it is one and not two. pub const fn to_px(self, len: Px) -> Px { let px = (self.px as i128) << (WIDE_REL + PX_SHIFT - WIDE_PX); let rel = self.rel as i128 * len.raw() as i128; Px::from_raw(((px + rel) >> WIDE_REL) as i32) } /// A *length* composed through the box it sits in, which is half of what /// the two ends of that box cost: where the parent sits falls out of the /// difference, so a length carries the parent's extent and its pixel span /// and nothing else. Two multiplies a level rather than four. pub const fn len_within(self, parent: &UiSpan) -> Self { let rel_span = (parent.end.rel.raw() - parent.start.rel.raw()) as i128; let px_span = (parent.end.px.raw() - parent.start.px.raw()) as i64; Self { rel: ((rel_span * self.rel as i128) >> REL_SHIFT) as i64, // The pixel term takes the fraction on the ordinary grid, which // keeps it inside an `i64`. Only the fraction itself compounds // multiplicatively and so needs the room: an error of a `Rel` // step here is that step times a pixel span, which is a ten // thousandth of a pixel over a whole window. px: self.px + ((px_span * (self.rel >> REL_GAIN)) >> (PX_SHIFT + REL_SHIFT - WIDE_PX)), } } } /// The two ends of a box, part-way through a composition. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct WideSpan { pub start: WideLen, pub end: WideLen, } impl WideSpan { pub const fn of(span: UiSpan) -> Self { Self { start: WideLen::of(span.start), end: WideLen::of(span.end), } } pub const fn within(self, parent: &UiSpan) -> Self { Self { start: self.start.within(parent), end: self.end.within(parent), } } } impl WideSpan { /// A box given as a part of this one: the same composition carried one /// level further, with the part on the ordinary grid and the frame it /// lands in already on the fine one. That is the way round a draw /// descends -- a widget states its child's box as a part of its own -- /// so composing this way never puts an intermediate back on the grid. pub const fn select(self, part: &UiSpan) -> Self { Self { start: self.end_at(part.start), end: self.end_at(part.end), } } /// How long such a part is, in half the multiplies its two ends cost: /// where this box sits falls out of the difference. pub const fn select_len(self, part: &UiSpan) -> WideLen { let len = part.len(); let rel_span = (self.end.rel - self.start.rel) as i128; let px_span = (self.end.px - self.start.px) >> PX_GAIN; WideLen { rel: ((rel_span * len.rel.raw() as i128) >> REL_SHIFT) as i64, px: ((len.px.raw() as i64) << PX_GAIN) + ((px_span * len.rel.raw() as i64) >> (REL_SHIFT - PX_GAIN)), } } const fn end_at(self, at: Len) -> WideLen { let rel_span = (self.end.rel - self.start.rel) as i128; let px_span = (self.end.px - self.start.px) >> PX_GAIN; WideLen { rel: self.start.rel + ((rel_span * at.rel.raw() as i128) >> REL_SHIFT) as i64, px: self.start.px + ((at.px.raw() as i64) << PX_GAIN) + ((px_span * at.rel.raw() as i64) >> (REL_SHIFT - PX_GAIN)), } } } /// How long a box is on each axis, part-way through a composition. What /// reads a box in pixels almost always wants this and not where it sits. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct WideSize { pub x: WideLen, pub y: WideLen, } impl WideSize { pub const fn of(region: UiRegion) -> Self { Self { x: WideLen::of(region.x.len()), y: WideLen::of(region.y.len()), } } pub const fn within(self, parent: &UiRegion) -> Self { Self { x: self.x.len_within(&parent.x), y: self.y.len_within(&parent.y), } } pub fn to_px(self, window: PxVec2) -> PxVec2 { PxVec2::new(self.x.to_px(window.x), self.y.to_px(window.y)) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct WideRegion { pub x: WideSpan, pub y: WideSpan, } impl WideRegion { /// A box given as a part of this one, on both axes. pub const fn select(self, part: &UiRegion) -> Self { Self { x: self.x.select(&part.x), y: self.y.select(&part.y), } } /// How big such a part is, which is what reads a box in pixels. pub const fn select_size(self, part: &UiRegion) -> WideSize { WideSize { x: self.x.select_len(&part.x), y: self.y.select_len(&part.y), } } pub const fn of(region: UiRegion) -> Self { Self { x: WideSpan::of(region.x), y: WideSpan::of(region.y), } } pub const fn within(self, parent: &UiRegion) -> Self { Self { x: self.x.within(&parent.x), y: self.y.within(&parent.y), } } pub fn to_px(self, window: PxVec2) -> PixelRegion { PixelRegion { top_left: PxVec2::new(self.x.start.to_px(window.x), self.y.start.to_px(window.y)), bot_right: PxVec2::new(self.x.end.to_px(window.x), self.y.end.to_px(window.y)), } } }