pub mod style;
pub use self::style::{BinPosition, BinStyle, BinVert, Color, ImageEffect};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BinID(pub(super) u64);
use std::collections::{BTreeMap, HashMap, HashSet};
use std::sync::atomic::{self, AtomicBool};
use std::sync::{Arc, Barrier, Weak};
use std::time::{Duration, Instant};
use arc_swap::ArcSwapAny;
use ilmenite::*;
use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock};
use crate::atlas::{
AtlasCacheCtrl, AtlasCoords, Image, ImageData, ImageDims, ImageType, SubImageCacheID,
};
use crate::image_view::BstImageView;
use crate::input::key::KeyCombo;
use crate::input::state::{LocalCursorState, LocalKeyState, WindowState};
use crate::input::{Char, InputHookCtrl, InputHookID, InputHookTarget, MouseButton};
pub use crate::interface::bin::style::BinStyleValidation;
use crate::interface::{scale_verts, ItfVertInfo};
use crate::interval::IntvlHookCtrl;
use crate::Basalt;
pub trait KeepAlive {}
impl KeepAlive for Arc<Bin> {}
impl KeepAlive for Bin {}
impl<T: KeepAlive> KeepAlive for Vec<T> {}
#[derive(Default, Debug, Clone, Copy)]
pub struct BinUpdateStats {
pub t_total: Duration,
pub t_hidden: Duration,
pub t_ancestors: Duration,
pub t_position: Duration,
pub t_zindex: Duration,
pub t_image: Duration,
pub t_opacity: Duration,
pub t_verts: Duration,
pub t_overflow: Duration,
pub t_scale: Duration,
pub t_callbacks: Duration,
pub t_style_obtain: Duration,
pub t_upcheck: Duration,
pub t_postset: Duration,
pub t_locks: Duration,
pub t_text: Duration,
pub t_ilmenite: Duration,
}
impl BinUpdateStats {
pub fn divide(self, amt: f32) -> Self {
BinUpdateStats {
t_total: self.t_total.div_f32(amt as f32),
t_hidden: self.t_hidden.div_f32(amt as f32),
t_ancestors: self.t_ancestors.div_f32(amt as f32),
t_position: self.t_position.div_f32(amt as f32),
t_zindex: self.t_zindex.div_f32(amt as f32),
t_image: self.t_image.div_f32(amt as f32),
t_opacity: self.t_opacity.div_f32(amt as f32),
t_verts: self.t_verts.div_f32(amt as f32),
t_overflow: self.t_overflow.div_f32(amt as f32),
t_scale: self.t_scale.div_f32(amt as f32),
t_callbacks: self.t_callbacks.div_f32(amt as f32),
t_style_obtain: self.t_style_obtain.div_f32(amt as f32),
t_upcheck: self.t_upcheck.div_f32(amt as f32),
t_postset: self.t_postset.div_f32(amt as f32),
t_locks: self.t_postset.div_f32(amt as f32),
t_text: self.t_text.div_f32(amt as f32),
t_ilmenite: self.t_ilmenite.div_f32(amt as f32),
}
}
pub fn average(stats: &Vec<BinUpdateStats>) -> BinUpdateStats {
let len = stats.len();
Self::sum(stats).divide(len as f32)
}
pub fn sum(stats: &Vec<BinUpdateStats>) -> BinUpdateStats {
let mut t_total = Duration::new(0, 0);
let mut t_hidden = Duration::new(0, 0);
let mut t_ancestors = Duration::new(0, 0);
let mut t_position = Duration::new(0, 0);
let mut t_zindex = Duration::new(0, 0);
let mut t_image = Duration::new(0, 0);
let mut t_opacity = Duration::new(0, 0);
let mut t_verts = Duration::new(0, 0);
let mut t_overflow = Duration::new(0, 0);
let mut t_scale = Duration::new(0, 0);
let mut t_callbacks = Duration::new(0, 0);
let mut t_style_obtain = Duration::new(0, 0);
let mut t_upcheck = Duration::new(0, 0);
let mut t_postset = Duration::new(0, 0);
let mut t_locks = Duration::new(0, 0);
let mut t_text = Duration::new(0, 0);
let mut t_ilmenite = Duration::new(0, 0);
for stat in stats {
t_total += stat.t_total;
t_hidden += stat.t_hidden;
t_ancestors += stat.t_ancestors;
t_position += stat.t_position;
t_zindex += stat.t_zindex;
t_image += stat.t_image;
t_opacity += stat.t_opacity;
t_verts += stat.t_verts;
t_overflow += stat.t_overflow;
t_scale += stat.t_scale;
t_callbacks += stat.t_callbacks;
t_style_obtain += stat.t_style_obtain;
t_upcheck += stat.t_upcheck;
t_postset += stat.t_postset;
t_locks += stat.t_locks;
t_text += stat.t_text;
t_ilmenite += stat.t_ilmenite;
}
BinUpdateStats {
t_total,
t_hidden,
t_ancestors,
t_position,
t_zindex,
t_image,
t_opacity,
t_verts,
t_overflow,
t_scale,
t_callbacks,
t_style_obtain,
t_upcheck,
t_postset,
t_locks,
t_text,
t_ilmenite,
}
}
}
#[derive(Default)]
struct BinHrchy {
parent: Option<Weak<Bin>>,
children: Vec<Weak<Bin>>,
}
#[derive(PartialEq, Eq, Hash)]
enum InternalHookTy {
Updated,
UpdatedOnce,
ChildrenAdded,
ChildrenRemoved,
}
enum InternalHookFn {
Updated(Box<dyn FnMut(&Arc<Bin>, &PostUpdate) + Send + 'static>),
ChildrenAdded(Box<dyn FnMut(&Arc<Bin>, &Vec<Arc<Bin>>) + Send + 'static>),
ChildrenRemoved(Box<dyn FnMut(&Arc<Bin>, &Vec<Weak<Bin>>) + Send + 'static>),
}
pub struct Bin {
basalt: Arc<Basalt>,
id: BinID,
hrchy: ArcSwapAny<Arc<BinHrchy>>,
style: ArcSwapAny<Arc<BinStyle>>,
initial: Mutex<bool>,
update: AtomicBool,
verts: Mutex<VertexState>,
post_update: RwLock<PostUpdate>,
input_hook_ids: Mutex<Vec<InputHookID>>,
keep_alive: Mutex<Vec<Arc<dyn KeepAlive + Send + Sync>>>,
last_update: Mutex<Instant>,
update_stats: Mutex<BinUpdateStats>,
internal_hooks: Mutex<HashMap<InternalHookTy, Vec<InternalHookFn>>>,
}
impl PartialEq for Bin {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.basalt, &other.basalt) && self.id == other.id
}
}
impl Eq for Bin {}
#[derive(Default)]
struct VertexState {
verts: Vec<(Vec<ItfVertInfo>, Option<Arc<BstImageView>>, u64)>,
#[allow(dead_code)]
atlas_coords_in_use: HashSet<AtlasCoords>,
}
#[derive(Clone, Default, Debug)]
pub struct PostUpdate {
pub tlo: [f32; 2],
pub tli: [f32; 2],
pub blo: [f32; 2],
pub bli: [f32; 2],
pub tro: [f32; 2],
pub tri: [f32; 2],
pub bro: [f32; 2],
pub bri: [f32; 2],
pub z_index: i16,
pub unbound_mm_y: [f32; 2],
pub unbound_mm_x: [f32; 2],
pub extent: [u32; 2],
pub scale: f32,
text_state: Option<BinTextState>,
}
#[derive(Debug, Clone)]
struct BinTextState {
x: f32,
y: f32,
style: BinTextStyle,
verts: BTreeMap<u64, Vec<ItfVertInfo>>,
atlas_coords_in_use: HashSet<AtlasCoords>,
glyphs: Vec<BinGlyphInfo>,
}
#[derive(Debug, Clone, PartialEq)]
struct BinTextStyle {
scale: f32,
text: String,
family: String,
weight: ImtWeight,
body_width: f32,
body_height: f32,
text_height: f32,
line_spacing: f32,
text_wrap: ImtTextWrap,
vert_align: ImtVertAlign,
hori_align: ImtHoriAlign,
}
#[allow(dead_code)]
#[derive(Clone, Default, Debug)]
struct BinGlyphInfo {
min_x: f32,
max_x: f32,
min_y: f32,
max_y: f32,
}
impl Drop for Bin {
fn drop(&mut self) {
for hook in self.input_hook_ids.lock().split_off(0) {
self.basalt.input_ref().remove_hook(hook);
}
let this_hrchy = self.hrchy.load_full();
if let Some(parent) = this_hrchy
.parent
.as_ref()
.and_then(|parent| parent.upgrade())
{
let parent_hrchy = parent.hrchy.load_full();
let mut children_removed = Vec::new();
let children = parent_hrchy
.children
.iter()
.filter_map(|child_wk| {
if child_wk.upgrade().is_some() {
Some(child_wk.clone())
} else {
children_removed.push(child_wk.clone());
None
}
})
.collect();
if !children_removed.is_empty() {
parent.hrchy.store(Arc::new(BinHrchy {
children,
parent: parent_hrchy.parent.clone(),
}));
parent.call_children_removed_hooks(children_removed);
}
}
self.basalt.interface_ref().composer_ref().unpark();
}
}
impl std::fmt::Debug for Bin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Bin").field(&self.id.0).finish()
}
}
impl Bin {
pub(crate) fn new(id: BinID, basalt: Arc<Basalt>) -> Arc<Self> {
Arc::new(Bin {
id,
basalt,
hrchy: ArcSwapAny::from(Arc::new(BinHrchy::default())),
style: ArcSwapAny::new(Arc::new(BinStyle::default())),
initial: Mutex::new(true),
update: AtomicBool::new(false),
verts: Mutex::new(VertexState::default()),
post_update: RwLock::new(PostUpdate::default()),
input_hook_ids: Mutex::new(Vec::new()),
keep_alive: Mutex::new(Vec::new()),
last_update: Mutex::new(Instant::now()),
update_stats: Mutex::new(BinUpdateStats::default()),
internal_hooks: Mutex::new(HashMap::from([
(InternalHookTy::Updated, Vec::new()),
(InternalHookTy::UpdatedOnce, Vec::new()),
(InternalHookTy::ChildrenAdded, Vec::new()),
(InternalHookTy::ChildrenRemoved, Vec::new()),
])),
})
}
pub fn basalt(&self) -> Arc<Basalt> {
self.basalt.clone()
}
pub fn basalt_ref(&self) -> &Arc<Basalt> {
&self.basalt
}
pub fn update_stats(&self) -> BinUpdateStats {
*self.update_stats.lock()
}
pub fn ancestors(&self) -> Vec<Arc<Bin>> {
let mut out = Vec::new();
let mut check_wk_op = self.hrchy.load_full().parent.clone();
while let Some(check_wk) = check_wk_op.take() {
if let Some(check) = check_wk.upgrade() {
out.push(check.clone());
check_wk_op = check.hrchy.load_full().parent.clone();
}
}
out
}
pub fn attach_input_hook(&self, hook_id: InputHookID) {
self.input_hook_ids.lock().push(hook_id);
}
pub fn on_press<C: KeyCombo, F>(self: &Arc<Self>, combo: C, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState, &LocalKeyState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_press()
.keys(combo)
.call(method)
.finish()
.unwrap()
}
pub fn on_release<C: KeyCombo, F>(self: &Arc<Self>, combo: C, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState, &LocalKeyState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_release()
.keys(combo)
.call(method)
.finish()
.unwrap()
}
pub fn on_hold<C: KeyCombo, F>(self: &Arc<Self>, combo: C, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &LocalKeyState, Option<Duration>) -> InputHookCtrl
+ Send
+ 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_hold()
.keys(combo)
.call(method)
.finish()
.unwrap()
}
pub fn on_character<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState, Char) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_character()
.call(method)
.finish()
.unwrap()
}
pub fn on_enter<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_enter()
.call(method)
.finish()
.unwrap()
}
pub fn on_leave<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_leave()
.call(method)
.finish()
.unwrap()
}
pub fn on_focus<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_focus()
.call(method)
.finish()
.unwrap()
}
pub fn on_focus_lost<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_focus_lost()
.call(method)
.finish()
.unwrap()
}
pub fn on_scroll<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState, f32, f32) -> InputHookCtrl + Send + 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_scroll()
.call(method)
.finish()
.unwrap()
}
pub fn on_cursor<F>(self: &Arc<Self>, method: F) -> InputHookID
where
F: FnMut(InputHookTarget, &WindowState, &LocalCursorState) -> InputHookCtrl
+ Send
+ 'static,
{
self.basalt
.input_ref()
.hook()
.bin(self)
.on_cursor()
.call(method)
.finish()
.unwrap()
}
#[inline]
pub fn on_children_added<F: FnMut(&Arc<Bin>, &Vec<Arc<Bin>>) + Send + 'static>(
self: &Arc<Self>,
func: F,
) {
self.internal_hooks
.lock()
.get_mut(&InternalHookTy::ChildrenAdded)
.unwrap()
.push(InternalHookFn::ChildrenAdded(Box::new(func)));
}
#[inline]
pub fn on_children_removed<F: FnMut(&Arc<Bin>, &Vec<Weak<Bin>>) + Send + 'static>(
self: &Arc<Self>,
func: F,
) {
self.internal_hooks
.lock()
.get_mut(&InternalHookTy::ChildrenRemoved)
.unwrap()
.push(InternalHookFn::ChildrenRemoved(Box::new(func)));
}
#[inline]
pub fn on_update<F: FnMut(&Arc<Bin>, &PostUpdate) + Send + 'static>(self: &Arc<Self>, func: F) {
self.internal_hooks
.lock()
.get_mut(&InternalHookTy::Updated)
.unwrap()
.push(InternalHookFn::Updated(Box::new(func)));
}
#[inline]
pub fn on_update_once<F: FnMut(&Arc<Bin>, &PostUpdate) + Send + 'static>(
self: &Arc<Self>,
func: F,
) {
self.internal_hooks
.lock()
.get_mut(&InternalHookTy::UpdatedOnce)
.unwrap()
.push(InternalHookFn::Updated(Box::new(func)));
}
pub fn wait_for_update(self: &Arc<Self>) {
let barrier = Arc::new(Barrier::new(2));
let barrier_copy = barrier.clone();
self.on_update_once(move |_, _| {
barrier_copy.wait();
});
barrier.wait();
}
pub fn last_update(&self) -> Instant {
*self.last_update.lock()
}
pub fn keep_alive(&self, thing: Arc<dyn KeepAlive + Send + Sync>) {
self.keep_alive.lock().push(thing);
}
pub fn parent(&self) -> Option<Arc<Bin>> {
self.hrchy
.load_full()
.parent
.as_ref()
.and_then(|v| v.upgrade())
}
pub fn children(&self) -> Vec<Arc<Bin>> {
self.hrchy
.load_full()
.children
.iter()
.filter_map(|wk| wk.upgrade())
.collect()
}
pub fn children_recursive(self: &Arc<Bin>) -> Vec<Arc<Bin>> {
let mut out = Vec::new();
let mut to_check = vec![self.clone()];
while !to_check.is_empty() {
let child = to_check.pop().unwrap();
to_check.append(&mut child.children());
out.push(child);
}
out
}
fn call_children_added_hooks(self: &Arc<Self>, children: Vec<Arc<Bin>>) {
for func_enum in self
.internal_hooks
.lock()
.get_mut(&InternalHookTy::ChildrenAdded)
.unwrap()
.iter_mut()
{
if let InternalHookFn::ChildrenAdded(func) = func_enum {
func(self, &children);
}
}
}
fn call_children_removed_hooks(self: &Arc<Self>, children: Vec<Weak<Bin>>) {
for func_enum in self
.internal_hooks
.lock()
.get_mut(&InternalHookTy::ChildrenRemoved)
.unwrap()
.iter_mut()
{
if let InternalHookFn::ChildrenRemoved(func) = func_enum {
func(self, &children);
}
}
}
pub fn add_child(self: &Arc<Self>, child: Arc<Bin>) {
let child_hrchy = child.hrchy.load_full();
child.hrchy.store(Arc::new(BinHrchy {
parent: Some(Arc::downgrade(self)),
children: child_hrchy.children.clone(),
}));
let this_hrchy = self.hrchy.load_full();
let mut children = this_hrchy.children.clone();
children.push(Arc::downgrade(&child));
self.hrchy.store(Arc::new(BinHrchy {
children,
parent: this_hrchy.parent.clone(),
}));
self.call_children_added_hooks(vec![child]);
}
pub fn add_children(self: &Arc<Self>, children: Vec<Arc<Bin>>) {
let this_hrchy = self.hrchy.load_full();
let mut this_children = this_hrchy.children.clone();
for child in children.iter() {
this_children.push(Arc::downgrade(child));
let child_hrchy = child.hrchy.load_full();
child.hrchy.store(Arc::new(BinHrchy {
parent: Some(Arc::downgrade(self)),
children: child_hrchy.children.clone(),
}));
}
self.hrchy.store(Arc::new(BinHrchy {
children: this_children,
parent: this_hrchy.parent.clone(),
}));
self.call_children_added_hooks(children);
}
pub fn take_children(self: &Arc<Self>) -> Vec<Arc<Bin>> {
let this_hrchy = self.hrchy.load_full();
let mut children = Vec::new();
for child in this_hrchy.children.iter() {
if let Some(child) = child.upgrade() {
let child_hrchy = child.hrchy.load_full();
child.hrchy.store(Arc::new(BinHrchy {
parent: None,
children: child_hrchy.children.clone(),
}));
children.push(child);
}
}
self.hrchy.store(Arc::new(BinHrchy {
children: Vec::new(),
parent: this_hrchy.parent.clone(),
}));
self.call_children_removed_hooks(this_hrchy.children.clone());
children
}
pub fn add_drag_events(self: &Arc<Self>, target_op: Option<Arc<Bin>>) {
#[derive(Default)]
struct Data {
target: Weak<Bin>,
mouse_x: f32,
mouse_y: f32,
pos_from_t: Option<f32>,
pos_from_b: Option<f32>,
pos_from_l: Option<f32>,
pos_from_r: Option<f32>,
}
let data = Arc::new(Mutex::new(None));
let target_wk = target_op
.map(|v| Arc::downgrade(&v))
.unwrap_or_else(|| Arc::downgrade(self));
let data_cp = data.clone();
self.on_press(MouseButton::Middle, move |_, window, _| {
let [mouse_x, mouse_y] = window.cursor_pos();
let style = match target_wk.upgrade() {
Some(bin) => bin.style_copy(),
None => return InputHookCtrl::Remove,
};
*data_cp.lock() = Some(Data {
target: target_wk.clone(),
mouse_x,
mouse_y,
pos_from_t: style.pos_from_t,
pos_from_b: style.pos_from_b,
pos_from_l: style.pos_from_l,
pos_from_r: style.pos_from_r,
});
Default::default()
});
let data_cp = data.clone();
self.attach_input_hook(
self.basalt
.input_ref()
.hook()
.window(&self.basalt.window())
.on_cursor()
.call(move |_, window, _| {
let [mouse_x, mouse_y] = window.cursor_pos();
let mut data_op = data_cp.lock();
let data = match &mut *data_op {
Some(some) => some,
None => return Default::default(),
};
let target = match data.target.upgrade() {
Some(some) => some,
None => return InputHookCtrl::Remove,
};
let dx = mouse_x - data.mouse_x;
let dy = mouse_y - data.mouse_y;
target
.style_update(BinStyle {
pos_from_t: data.pos_from_t.as_ref().map(|v| *v + dy),
pos_from_b: data.pos_from_b.as_ref().map(|v| *v - dy),
pos_from_l: data.pos_from_l.as_ref().map(|v| *v + dx),
pos_from_r: data.pos_from_r.as_ref().map(|v| *v - dx),
..target.style_copy()
})
.expect_valid();
target.update_children();
Default::default()
})
.finish()
.unwrap(),
);
self.on_release(MouseButton::Middle, move |_, _, _| {
*data.lock() = None;
Default::default()
});
}
pub fn add_enter_text_events(self: &Arc<Self>) {
self.on_character(move |target, _, c| {
let this = target.into_bin().unwrap();
let mut style = this.style_copy();
c.modify_string(&mut style.text);
this.style_update(style).expect_valid();
Default::default()
});
}
pub fn add_button_fade_events(self: &Arc<Self>) {
}
pub fn fade_out(self: &Arc<Self>, millis: u64) {
let bin_wk = Arc::downgrade(self);
let start_opacity = self.style_copy().opacity.unwrap_or(1.0);
let steps = (millis / 8) as i64;
let step_size = start_opacity / steps as f32;
let mut step_i = 0;
self.basalt
.interval_ref()
.do_every(Duration::from_millis(8), None, move |_| {
if step_i > steps {
return IntvlHookCtrl::Remove;
}
let bin = match bin_wk.upgrade() {
Some(some) => some,
None => return IntvlHookCtrl::Remove,
};
let opacity = start_opacity - (step_i as f32 * step_size);
let mut copy = bin.style_copy();
copy.opacity = Some(opacity);
if step_i == steps {
copy.hidden = Some(true);
}
bin.style_update(copy).expect_valid();
bin.update_children();
step_i += 1;
Default::default()
});
}
pub fn fade_in(self: &Arc<Self>, millis: u64, target: f32) {
let bin_wk = Arc::downgrade(self);
let start_opacity = self.style_copy().opacity.unwrap_or(1.0);
let steps = (millis / 8) as i64;
let step_size = (target - start_opacity) / steps as f32;
let mut step_i = 0;
self.basalt
.interval_ref()
.do_every(Duration::from_millis(8), None, move |_| {
if step_i > steps {
return IntvlHookCtrl::Remove;
}
let bin = match bin_wk.upgrade() {
Some(some) => some,
None => return IntvlHookCtrl::Remove,
};
let opacity = (step_i as f32 * step_size) + start_opacity;
let mut copy = bin.style_copy();
copy.opacity = Some(opacity);
copy.hidden = Some(false);
bin.style_update(copy).expect_valid();
bin.update_children();
step_i += 1;
Default::default()
});
}
pub fn calc_vert_overflow(self: &Arc<Bin>) -> f32 {
let self_post_up = self.post_update.read();
let display_min = self_post_up.tli[1];
let display_max = self_post_up.bli[1];
let mut content_min = self_post_up.unbound_mm_y[0];
let mut content_max = self_post_up.unbound_mm_y[1];
for child in self.children() {
let child_post_up = child.post_update.read();
content_min = content_min.min(child_post_up.unbound_mm_y[0]);
content_max = content_max.max(child_post_up.unbound_mm_y[1]);
}
let overflow_top = display_min - content_min;
let overflow_bottom = content_max - display_max + self.style().pad_b.unwrap_or(0.0);
overflow_top + overflow_bottom
}
pub fn calc_hori_overflow(self: &Arc<Bin>) -> f32 {
let self_post_up = self.post_update.read();
let display_min = self_post_up.tli[0];
let display_max = self_post_up.tri[0];
let mut content_min = self_post_up.unbound_mm_x[0];
let mut content_max = self_post_up.unbound_mm_x[1];
for child in self.children() {
let child_post_up = child.post_update.read();
content_min = content_min.min(child_post_up.unbound_mm_x[0]);
content_max = content_max.max(child_post_up.unbound_mm_x[1]);
}
let overflow_left = display_min - content_min;
let overflow_right = content_max - display_max + self.style().pad_r.unwrap_or(0.0);
overflow_left + overflow_right
}
pub fn post_update(&self) -> PostUpdate {
self.post_update.read().clone()
}
pub fn id(&self) -> BinID {
self.id
}
pub fn mouse_inside(&self, mouse_x: f32, mouse_y: f32) -> bool {
if self.is_hidden(None) {
return false;
}
let post = self.post_update.read();
if mouse_x >= post.tlo[0]
&& mouse_x <= post.tro[0]
&& mouse_y >= post.tlo[1]
&& mouse_y <= post.blo[1]
{
return true;
}
false
}
fn pos_size_tlwh(&self, win_size_: Option<[f32; 2]>) -> (f32, f32, f32, f32) {
let win_size = win_size_.unwrap_or([0.0, 0.0]);
let style = self.style();
if *self.initial.lock() {
return (0.0, 0.0, 0.0, 0.0);
}
let (par_t, par_b, par_l, par_r) = match style.position.unwrap_or(BinPosition::Window) {
BinPosition::Window => (0.0, win_size[1], 0.0, win_size[0]),
BinPosition::Parent => {
match self.parent() {
Some(ref parent) => {
let (top, left, width, height) = parent.pos_size_tlwh(win_size_);
(top, top + height, left, left + width)
},
None => (0.0, win_size[1], 0.0, win_size[0]),
}
},
BinPosition::Floating => {
let parent = match self.parent() {
Some(some) => some,
None => {
unreachable!("No parent on floating Bin")
},
};
let (parent_t, parent_l, parent_w, parent_h) = parent.pos_size_tlwh(win_size_);
let parent_style = parent.style_copy();
let parent_pad_t = parent_style.pad_t.unwrap_or(0.0);
let parent_pad_b = parent_style.pad_b.unwrap_or(0.0);
let parent_pad_l = parent_style.pad_l.unwrap_or(0.0);
let parent_pad_r = parent_style.pad_r.unwrap_or(0.0);
let usable_width = parent_w - parent_pad_l - parent_pad_r;
let usable_height = parent_h - parent_pad_t - parent_pad_b;
struct Sibling {
order: u64,
width: f32,
height: f32,
margin_t: f32,
margin_b: f32,
margin_l: f32,
margin_r: f32,
}
let mut sibling_order = 0;
let mut order_op = None;
let mut siblings = Vec::new();
for sibling in parent.children().into_iter() {
if sibling.id() == self.id {
order_op = Some(sibling_order);
sibling_order += 1;
continue;
}
let sibling_style = sibling.style_copy();
let mut sibling_width = match sibling_style.width {
Some(some) => some,
None => {
match sibling_style.width_pct {
Some(some) => some * usable_width,
None => {
unreachable!("'width' or 'width_pct' is not defined.")
},
}
},
};
let mut sibling_height = match sibling_style.height {
Some(some) => some,
None => {
match sibling_style.height_pct {
Some(some) => some * usable_height,
None => {
unreachable!("'height' or 'height_pct' is not defined.")
},
}
},
};
sibling_width += sibling_style.width_offset.unwrap_or(0.0);
sibling_height += sibling_style.height_offset.unwrap_or(0.0);
siblings.push(Sibling {
order: sibling_order,
width: sibling_width,
height: sibling_height,
margin_t: sibling_style.margin_t.unwrap_or(0.0),
margin_b: sibling_style.margin_b.unwrap_or(0.0),
margin_l: sibling_style.margin_l.unwrap_or(0.0),
margin_r: sibling_style.margin_r.unwrap_or(0.0),
});
sibling_order += 1;
}
assert!(order_op.is_some(), "Bin is not a child of parent.");
let order = order_op.unwrap();
let mut current_x = 0.0;
let mut current_y = 0.0;
let mut row_height = 0.0;
let mut row_items = 0;
for sibling in siblings {
if sibling.order > order {
break;
}
let add_width = sibling.margin_l + sibling.width + sibling.margin_r;
let height = sibling.margin_t + sibling.height + sibling.margin_b;
if add_width >= usable_width {
if row_items > 0 {
current_y += row_height;
row_items = 0;
}
current_x = 0.0;
current_y += height;
} else if current_x + add_width >= usable_width {
if row_items > 0 {
current_y += row_height;
row_items = 0;
}
current_x = add_width;
row_height = height;
} else {
current_x += add_width;
if height > row_height {
row_height = height;
}
}
row_items += 1;
}
let mut width = match style.width {
Some(some) => some,
None => {
match style.width_pct {
Some(some) => (some / 100.0) * usable_width,
None => {
unreachable!("'width' or 'width_pct' is not defined.")
},
}
},
};
let mut height = match style.height {
Some(some) => some,
None => {
match style.height_pct {
Some(some) => (some / 100.0) * usable_height,
None => {
unreachable!("'height' or 'height_pct' is not defined.")
},
}
},
};
width += style.width_offset.unwrap_or(0.0);
height += style.height_offset.unwrap_or(0.0);
let margin_l = style.margin_l.unwrap_or(0.0);
let margin_r = style.margin_r.unwrap_or(0.0);
let margin_t = style.margin_t.unwrap_or(0.0);
let add_width = margin_l + width + margin_r;
if current_x + add_width >= usable_width {
if row_items > 0 {
current_y += row_height;
}
let top = parent_t + parent_pad_t + current_y + margin_t;
let left = parent_l + parent_pad_l + margin_l;
return (top, left, width, height);
}
let top = parent_t + parent_pad_t + margin_t + current_y;
let left = parent_l + parent_pad_l + margin_l + current_x;
return (top, left, width, height);
},
};
let pos_from_t = match style.pos_from_t {
Some(some) => Some(some),
None => {
style
.pos_from_t_pct
.map(|some| (some / 100.0) * (par_b - par_t))
},
};
let pos_from_b = match style.pos_from_b {
Some(some) => Some(some),
None => {
style
.pos_from_b_pct
.map(|some| (some / 100.0) * (par_b - par_t))
},
}
.map(|v| v + style.pos_from_b_offset.unwrap_or(0.0));
let pos_from_l = match style.pos_from_l {
Some(some) => Some(some),
None => {
style
.pos_from_l_pct
.map(|some| (some / 100.0) * (par_r - par_l))
},
};
let pos_from_r = match style.pos_from_r {
Some(some) => Some(some),
None => {
style
.pos_from_r_pct
.map(|some| (some / 100.0) * (par_r - par_l))
},
}
.map(|v| v + style.pos_from_r_offset.unwrap_or(0.0));
let from_t = match pos_from_t {
Some(from_t) => par_t + from_t,
None => {
match pos_from_b {
Some(from_b) => {
match style.height {
Some(height) => par_b - from_b - height,
None => {
unreachable!("Invalid position/dimension.")
},
}
},
None => {
unreachable!("Invalid position/dimension.")
},
}
},
} + style.pos_from_t_offset.unwrap_or(0.0);
let from_l = match pos_from_l {
Some(from_l) => from_l + par_l,
None => {
match pos_from_r {
Some(from_r) => {
match style.width {
Some(width) => par_r - from_r - width,
None => {
unreachable!("Invalid position/dimension.")
},
}
},
None => {
unreachable!("Invalid position/dimension.")
},
}
},
} + style.pos_from_l_offset.unwrap_or(0.0);
let width_offset = style.width_offset.unwrap_or(0.0);
let width = {
if let Some(pos_from_r) = pos_from_l.and(pos_from_r) {
par_r - pos_from_r - from_l
} else {
match style.width {
Some(some) => some + width_offset,
None => {
match style.width_pct {
Some(some) => ((some / 100.0) * (par_r - par_l)) + width_offset,
None => {
unreachable!("Invalid position/dimension.")
},
}
},
}
}
};
let height_offset = style.height_offset.unwrap_or(0.0);
let height = {
if let Some(pos_from_b) = pos_from_t.and(pos_from_b) {
par_b - pos_from_b - from_t
} else {
match style.height {
Some(some) => some + height_offset,
None => {
match style.height_pct {
Some(some) => ((some / 100.0) * (par_b - par_t)) + height_offset,
None => {
unreachable!("Invalid position/dimension.")
},
}
},
}
}
};
(from_t, from_l, width, height)
}
pub fn visible(&self) -> bool {
!self.is_hidden(None)
}
pub fn toggle_hidden(&self) {
let mut style = self.style_copy();
style.hidden = Some(!style.hidden.unwrap_or(false));
self.style_update(style).expect_valid();
}
fn is_hidden(&self, style_: Option<&BinStyle>) -> bool {
match match style_ {
Some(style) => style.hidden.unwrap_or(false),
None => self.style().hidden.unwrap_or(false),
} {
true => true,
false => {
match self.parent() {
Some(parent) => parent.is_hidden(None),
None => false,
}
},
}
}
pub(crate) fn verts_cp(&self) -> Vec<(Vec<ItfVertInfo>, Option<Arc<BstImageView>>, u64)> {
self.verts.lock().verts.clone()
}
pub(crate) fn wants_update(&self) -> bool {
self.update.load(atomic::Ordering::SeqCst)
}
pub(crate) fn do_update(self: &Arc<Self>, win_size: [f32; 2], scale: f32) {
let update_stats = self.basalt.show_bin_stats();
let mut stats = BinUpdateStats::default();
let mut inst = Instant::now();
if *self.initial.lock() {
return;
}
self.update.store(false, atomic::Ordering::SeqCst);
if update_stats {
stats.t_upcheck = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let prev_update = self.post_update();
let style = self.style();
let scaled_win_size = [win_size[0] / scale, win_size[1] / scale];
if update_stats {
stats.t_style_obtain = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
if self.is_hidden(Some(&style)) {
*self.verts.lock() = VertexState::default();
*self.last_update.lock() = Instant::now();
self.post_update.write().text_state = None;
return;
}
if update_stats {
stats.t_hidden = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let ancestor_data: Vec<(Arc<Bin>, Arc<BinStyle>, f32, f32, f32, f32)> = self
.ancestors()
.into_iter()
.map(|bin| {
let (top, left, width, height) = bin.pos_size_tlwh(Some(scaled_win_size));
(bin.clone(), bin.style(), top, left, width, height)
})
.collect();
if update_stats {
stats.t_ancestors = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let (top, left, width, height) = self.pos_size_tlwh(Some(scaled_win_size));
let border_size_t = style.border_size_t.unwrap_or(0.0);
let border_size_b = style.border_size_b.unwrap_or(0.0);
let border_size_l = style.border_size_l.unwrap_or(0.0);
let border_size_r = style.border_size_r.unwrap_or(0.0);
let mut border_color_t = style.border_color_t.clone().unwrap_or(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
});
let mut border_color_b = style.border_color_b.clone().unwrap_or(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
});
let mut border_color_l = style.border_color_l.clone().unwrap_or(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
});
let mut border_color_r = style.border_color_r.clone().unwrap_or(Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
});
let mut back_color = style.back_color.clone().unwrap_or(Color {
r: 0.0,
b: 0.0,
g: 0.0,
a: 0.0,
});
if update_stats {
stats.t_position = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let z_index = match style.z_index.as_ref() {
Some(some) => *some,
None => {
let mut z_index_op = None;
let mut checked = 0;
for (_, check_style, ..) in &ancestor_data {
match check_style.z_index.as_ref() {
Some(some) => {
z_index_op = Some(*some + checked + 1);
break;
},
None => {
checked += 1;
},
}
}
z_index_op.unwrap_or(ancestor_data.len() as i16)
},
} + style.add_z_index.unwrap_or(0);
if update_stats {
stats.t_zindex = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let mut bps = PostUpdate {
tlo: [left - border_size_l, top - border_size_t],
tli: [left, top],
blo: [left - border_size_l, top + height + border_size_b],
bli: [left, top + height],
tro: [left + width + border_size_r, top - border_size_t],
tri: [left + width, top],
bro: [left + width + border_size_r, top + height + border_size_b],
bri: [left + width, top + height],
z_index,
unbound_mm_y: [top, top + height],
unbound_mm_x: [left, left + width],
text_state: None,
extent: [win_size[0].trunc() as u32, win_size[1].trunc() as u32],
scale,
};
if update_stats {
stats.t_postset = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let back_image_cache = style.back_image_cache.unwrap_or_default();
let (back_img, back_coords) = match style.back_image.as_ref() {
Some(path) => {
match self
.basalt
.atlas_ref()
.load_image_from_path(back_image_cache, path)
{
Ok(coords) => (None, coords),
Err(e) => {
println!(
"[Basalt]: Bin ID: {:?} | failed to load image into atlas {}: {}",
self.id, path, e
);
(None, AtlasCoords::none())
},
}
},
None => {
match style.back_image_url.as_ref() {
Some(url) => {
match self
.basalt
.atlas_ref()
.load_image_from_url(back_image_cache, url)
{
Ok(coords) => (None, coords),
Err(e) => {
println!(
"[Basalt]: Bin ID: {:?} | failed to load image into atlas {}: \
{}",
self.id, url, e
);
(None, AtlasCoords::none())
},
}
},
None => {
match style.back_image_atlas.clone() {
Some(coords) => (None, coords),
None => {
match style.back_image_raw.as_ref() {
Some(image) => {
let coords = match style.back_image_raw_coords.as_ref() {
Some(some) => some.clone(),
None => {
let dims = image.dimensions();
AtlasCoords::external(
0.0,
0.0,
dims.width() as f32,
dims.height() as f32,
)
},
};
(Some(image.clone()), coords)
},
None => (None, AtlasCoords::none()),
}
},
}
},
}
},
};
let back_img_vert_ty = match style.back_image_effect.as_ref() {
Some(some) => some.vert_type(),
None => 100,
};
if update_stats {
stats.t_image = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let mut opacity = style.opacity.unwrap_or(1.0);
for (_, check_style, ..) in &ancestor_data {
opacity *= check_style.opacity.unwrap_or(1.0);
}
if opacity != 1.0 {
border_color_t.a *= opacity;
border_color_b.a *= opacity;
border_color_l.a *= opacity;
border_color_r.a *= opacity;
back_color.a *= opacity;
}
if update_stats {
stats.t_opacity = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let base_z = (-z_index as i32 + i16::max_value() as i32) as f32 / i32::max_value() as f32;
let content_z =
(-(z_index + 1) as i32 + i16::max_value() as i32) as f32 / i32::max_value() as f32;
let mut verts = Vec::with_capacity(54);
let border_radius_tl = style.border_radius_tl.unwrap_or(0.0);
let border_radius_tr = style.border_radius_tr.unwrap_or(0.0);
let border_radius_bl = style.border_radius_bl.unwrap_or(0.0);
let border_radius_br = style.border_radius_br.unwrap_or(0.0);
if border_radius_tl != 0.0
|| border_radius_tr != 0.0
|| border_radius_bl != 0.0
|| border_radius_br != 0.0
{
let border_radius_tmax = if border_radius_tl > border_radius_tr {
border_radius_tl
} else {
border_radius_tr
};
let border_radius_bmax = if border_radius_bl > border_radius_br {
border_radius_bl
} else {
border_radius_br
};
if back_color.a > 0.0 || !back_coords.is_none() || back_img.is_some() {
let mut back_verts = Vec::new();
if border_radius_tl != 0.0 || border_radius_tr != 0.0 {
back_verts.push([bps.tri[0] - border_radius_tr, bps.tri[1]]);
back_verts.push([bps.tli[0] + border_radius_tl, bps.tli[1]]);
back_verts.push([
bps.tli[0] + border_radius_tl,
bps.tli[1] + border_radius_tmax,
]);
back_verts.push([bps.tri[0] - border_radius_tr, bps.tri[1]]);
back_verts.push([
bps.tli[0] + border_radius_tl,
bps.tli[1] + border_radius_tmax,
]);
back_verts.push([
bps.tri[0] - border_radius_tr,
bps.tri[1] + border_radius_tmax,
]);
if border_radius_tl > border_radius_tr {
back_verts.push([bps.tri[0], bps.tri[1] + border_radius_tr]);
back_verts
.push([bps.tri[0] - border_radius_tr, bps.tri[1] + border_radius_tr]);
back_verts.push([
bps.tri[0] - border_radius_tr,
bps.tri[1] + border_radius_tmax,
]);
back_verts.push([bps.tri[0], bps.tri[1] + border_radius_tr]);
back_verts.push([
bps.tri[0] - border_radius_tr,
bps.tri[1] + border_radius_tmax,
]);
back_verts.push([bps.tri[0], bps.tri[1] + border_radius_tmax]);
} else if border_radius_tr > border_radius_tl {
back_verts
.push([bps.tli[0] + border_radius_tl, bps.tli[1] + border_radius_tl]);
back_verts.push([bps.tli[0], bps.tli[1] + border_radius_tl]);
back_verts.push([bps.tli[0], bps.tli[1] + border_radius_tmax]);
back_verts
.push([bps.tli[0] + border_radius_tl, bps.tli[1] + border_radius_tl]);
back_verts.push([bps.tli[0], bps.tli[1] + border_radius_tmax]);
back_verts.push([
bps.tli[0] + border_radius_tl,
bps.tli[1] + border_radius_tmax,
]);
}
}
if border_radius_bl != 0.0 || border_radius_br != 0.0 {
back_verts.push([
bps.bri[0] - border_radius_br,
bps.bri[1] - border_radius_bmax,
]);
back_verts.push([
bps.bli[0] + border_radius_bl,
bps.bli[1] - border_radius_bmax,
]);
back_verts.push([bps.bli[0] + border_radius_bl, bps.bli[1]]);
back_verts.push([
bps.bri[0] - border_radius_br,
bps.bri[1] - border_radius_bmax,
]);
back_verts.push([bps.bli[0] + border_radius_bl, bps.bli[1]]);
back_verts.push([bps.bri[0] - border_radius_br, bps.bri[1]]);
if border_radius_bl > border_radius_br {
back_verts.push([bps.bri[0], bps.bri[1] - border_radius_bmax]);
back_verts.push([
bps.bri[0] - border_radius_br,
bps.bri[1] - border_radius_bmax,
]);
back_verts
.push([bps.bri[0] - border_radius_br, bps.bri[1] - border_radius_br]);
back_verts.push([bps.bri[0], bps.bri[1] - border_radius_bmax]);
back_verts
.push([bps.bri[0] - border_radius_br, bps.bri[1] - border_radius_br]);
back_verts.push([bps.bri[0], bps.bri[1] - border_radius_br]);
} else if border_radius_br > border_radius_bl {
back_verts.push([
bps.bli[0] + border_radius_bl,
bps.bli[1] - border_radius_bmax,
]);
back_verts.push([bps.bli[0], bps.bli[1] - border_radius_bmax]);
back_verts.push([bps.bli[0], bps.bli[1] - border_radius_bl]);
back_verts.push([
bps.bli[0] + border_radius_bl,
bps.bli[1] - border_radius_bmax,
]);
back_verts.push([bps.bli[0], bps.bli[1] - border_radius_bl]);
back_verts
.push([bps.bli[0] + border_radius_bl, bps.bli[1] - border_radius_bl]);
}
}
if border_radius_tl != 0.0 {
let a = (bps.tli[0], bps.tli[1] + border_radius_tl);
let b = (bps.tli[0], bps.tli[1]);
let c = (bps.tli[0] + border_radius_tl, bps.tli[1]);
let dx = bps.tli[0] + border_radius_tl;
let dy = bps.tli[1] + border_radius_tl;
for ((ax, ay), (bx, by)) in curve_line_segments(a, b, c) {
back_verts.push([dx, dy]);
back_verts.push([bx, by]);
back_verts.push([ax, ay]);
}
}
if border_radius_tr != 0.0 {
let a = (bps.tri[0], bps.tri[1] + border_radius_tr);
let b = (bps.tri[0], bps.tri[1]);
let c = (bps.tri[0] - border_radius_tr, bps.tri[1]);
let dx = bps.tri[0] - border_radius_tr;
let dy = bps.tri[1] + border_radius_tr;
for ((ax, ay), (bx, by)) in curve_line_segments(a, b, c) {
back_verts.push([dx, dy]);
back_verts.push([bx, by]);
back_verts.push([ax, ay]);
}
}
if border_radius_bl != 0.0 {
let a = (bps.bli[0], bps.bli[1] - border_radius_bl);
let b = (bps.bli[0], bps.bli[1]);
let c = (bps.bli[0] + border_radius_bl, bps.bli[1]);
let dx = bps.bli[0] + border_radius_bl;
let dy = bps.bli[1] - border_radius_bl;
for ((ax, ay), (bx, by)) in curve_line_segments(a, b, c) {
back_verts.push([dx, dy]);
back_verts.push([bx, by]);
back_verts.push([ax, ay]);
}
}
if border_radius_br != 0.0 {
let a = (bps.bri[0], bps.bri[1] - border_radius_br);
let b = (bps.bri[0], bps.bri[1]);
let c = (bps.bri[0] - border_radius_br, bps.bri[1]);
let dx = bps.bri[0] - border_radius_br;
let dy = bps.bri[1] - border_radius_br;
for ((ax, ay), (bx, by)) in curve_line_segments(a, b, c) {
back_verts.push([dx, dy]);
back_verts.push([bx, by]);
back_verts.push([ax, ay]);
}
}
back_verts.push([bps.tri[0], bps.tri[1] + border_radius_tmax]);
back_verts.push([bps.tli[0], bps.tli[1] + border_radius_tmax]);
back_verts.push([bps.bli[0], bps.bli[1] - border_radius_bmax]);
back_verts.push([bps.tri[0], bps.tri[1] + border_radius_tmax]);
back_verts.push([bps.bli[0], bps.bli[1] - border_radius_bmax]);
back_verts.push([bps.bri[0], bps.bri[1] - border_radius_bmax]);
let ty = if !back_coords.is_none() || back_img.is_some() {
back_img_vert_ty
} else {
0
};
let bc_tlwh = back_coords.tlwh();
for [x, y] in back_verts {
let coords_x = (((x - bps.tli[0]) / (bps.tri[0] - bps.tli[0]))
* bc_tlwh[2] as f32)
+ bc_tlwh[0] as f32;
let coords_y = (((y - bps.tli[1]) / (bps.bli[1] - bps.tli[1]))
* bc_tlwh[3] as f32)
+ bc_tlwh[1] as f32;
verts.push(ItfVertInfo {
position: [x, y, base_z],
coords: [coords_x, coords_y],
color: back_color.as_array(),
ty,
tex_i: 0,
});
}
}
} else {
if border_color_t.a > 0.0 && border_size_t > 0.0 {
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tro[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tlo[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tro[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_b.a > 0.0 && border_size_b > 0.0 {
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.blo[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.blo[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bro[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_l.a > 0.0 && border_size_l > 0.0 {
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tlo[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.blo[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.blo[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_r.a > 0.0 && border_size_r > 0.0 {
verts.push(ItfVertInfo {
position: [bps.tro[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tro[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bro[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_t.a > 0.0
&& border_size_t > 0.0
&& border_color_l.a > 0.0
&& border_size_l > 0.0
{
verts.push(ItfVertInfo {
position: [bps.tlo[0], bps.tlo[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tlo[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tlo[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tlo[0], bps.tlo[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_t.a > 0.0
&& border_size_t > 0.0
&& border_color_r.a > 0.0
&& border_size_r > 0.0
{
verts.push(ItfVertInfo {
position: [bps.tro[0], bps.tro[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tro[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tro[0], bps.tro[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tro[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: [0.0, 0.0],
color: border_color_t.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_b.a > 0.0
&& border_size_b > 0.0
&& border_color_l.a > 0.0
&& border_size_l > 0.0
{
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.blo[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.blo[0], bps.blo[1], base_z],
coords: [0.0, 0.0],
color: border_color_l.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.blo[0], bps.blo[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.blo[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
}
if border_color_b.a > 0.0
&& border_size_b > 0.0
&& border_color_r.a > 0.0
&& border_size_r > 0.0
{
verts.push(ItfVertInfo {
position: [bps.bro[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bro[0], bps.bro[1], base_z],
coords: [0.0, 0.0],
color: border_color_r.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bro[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bro[0], bps.bro[1], base_z],
coords: [0.0, 0.0],
color: border_color_b.as_array(),
ty: 0,
tex_i: 0,
});
}
if back_color.a > 0.0 || !back_coords.is_none() || back_img.is_some() {
let ty = if !back_coords.is_none() || back_img.is_some() {
back_img_vert_ty
} else {
0
};
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: back_coords.top_right(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tli[0], bps.tli[1], base_z],
coords: back_coords.top_left(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: back_coords.bottom_left(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.tri[0], bps.tri[1], base_z],
coords: back_coords.top_right(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bli[0], bps.bli[1], base_z],
coords: back_coords.bottom_left(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [bps.bri[0], bps.bri[1], base_z],
coords: back_coords.bottom_right(),
color: back_color.as_array(),
ty,
tex_i: 0,
});
}
}
for &BinVert {
ref position,
ref color,
} in &style.custom_verts
{
let z = if position.2 == 0 {
content_z
} else {
(-(z_index + position.2) as i32 + i16::max_value() as i32) as f32
/ i32::max_value() as f32
};
verts.push(ItfVertInfo {
position: [bps.tli[0] + position.0, bps.tli[1] + position.1, z],
coords: [0.0, 0.0],
color: color.as_array(),
ty: 0,
tex_i: 0,
});
}
let mut vert_data = vec![(verts, back_img, back_coords.image_id())];
let mut atlas_coords_in_use = HashSet::new();
if !back_coords.is_none() && !back_coords.is_external() {
atlas_coords_in_use.insert(back_coords);
}
if update_stats {
stats.t_verts = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
if !style.text.is_empty() {
#[allow(clippy::never_loop)]
loop {
let pad_t = style.pad_t.unwrap_or(0.0);
let pad_b = style.pad_b.unwrap_or(0.0);
let pad_l = style.pad_l.unwrap_or(0.0);
let pad_r = style.pad_r.unwrap_or(0.0);
let body_width = bps.tri[0] - bps.tli[0] - pad_l - pad_r;
let body_height = bps.bli[1] - bps.tli[1] - pad_t - pad_b;
let mut color = style
.text_color
.clone()
.unwrap_or_else(|| Color::srgb_hex("000000"));
color.a *= opacity;
let text_height = style.text_height.unwrap_or(12.0);
let text_wrap = style.text_wrap.unwrap_or(ImtTextWrap::NewLine);
let vert_align = style.text_vert_align.unwrap_or(ImtVertAlign::Top);
let hori_align = style.text_hori_align.unwrap_or(ImtHoriAlign::Left);
let line_spacing = style.line_spacing.unwrap_or(0.0);
let (font_family, font_weight) =
if style.font_family.is_none() || style.font_weight.is_none() {
let (default_font_family, default_font_weight) =
match self.basalt.interface_ref().default_font() {
Some(some) => some,
None => {
unreachable!("No default font.")
},
};
(
style.font_family.clone().unwrap_or(default_font_family),
style.font_weight.unwrap_or(default_font_weight),
)
} else {
(
style.font_family.clone().unwrap(),
style.font_weight.unwrap(),
)
};
let text = if style.text_secret.unwrap_or(false) {
(0..style.text.len()).into_iter().map(|_| '*').collect()
} else {
style.text.clone()
};
let mut text_state = BinTextState {
x: bps.tli[0] + pad_l,
y: bps.tli[1] + pad_t,
style: BinTextStyle {
scale,
text: text.clone(),
family: font_family,
weight: font_weight,
body_width,
body_height,
text_height,
line_spacing,
text_wrap,
vert_align,
hori_align,
},
verts: BTreeMap::new(),
glyphs: Vec::new(),
atlas_coords_in_use: HashSet::new(),
};
if prev_update.text_state.is_some()
&& prev_update.text_state.as_ref().unwrap().style == text_state.style
{
let prev_text_state = prev_update.text_state.as_ref().unwrap();
let trans_x = text_state.x - prev_text_state.x;
let trans_y = text_state.y - prev_text_state.y;
text_state.atlas_coords_in_use = prev_text_state.atlas_coords_in_use.clone();
for (atlas_i, prev_verts) in prev_text_state.verts.iter() {
let verts = text_state.verts.entry(*atlas_i).or_insert_with(Vec::new);
for vert in prev_verts {
verts.push(ItfVertInfo {
position: [
vert.position[0] + trans_x,
vert.position[1] + trans_y,
content_z,
],
coords: vert.coords,
color: color.as_array(),
ty: 2,
tex_i: 0,
});
}
}
} else {
let glyphs = match self.basalt.interface_ref().ilmenite().glyphs_for_text(
text_state.style.family.clone(),
text_state.style.weight,
text_height * scale,
Some(ImtShapeOpts {
body_width: body_width * scale,
body_height: body_height * scale,
text_height: text_height * scale,
line_spacing: line_spacing * scale,
text_wrap,
vert_align,
hori_align,
..ImtShapeOpts::default()
}),
text.clone(),
) {
Ok(ok) => ok,
Err(ImtError {
src: ImtErrorSrc::Shaper,
ty: ImtErrorTy::Other(_),
}) => break,
Err(e) => {
println!(
"[Basalt]: Bin ID: {:?} | Failed to render text: {:?} | Text: \
\"{}\"",
self.id, e, text
);
break;
},
};
if update_stats {
stats.t_ilmenite = inst.elapsed();
}
let cached_coords = self.basalt.atlas_ref().batch_cache_coords(
glyphs
.iter()
.map(|glyph| {
SubImageCacheID::Glyph {
family: glyph.family.clone(),
weight: glyph.weight,
code: glyph.index,
height: OrderedFloat::from(text_height * scale),
}
})
.collect(),
);
for (glyph, coords_op) in glyphs.into_iter().zip(cached_coords.into_iter()) {
let coords = if glyph.w == 0 || glyph.h == 0 || glyph.bitmap.is_none() {
continue;
} else {
match coords_op {
Some(some) => some,
None => {
let cache_id = SubImageCacheID::Glyph {
family: glyph.family,
weight: glyph.weight,
code: glyph.index,
height: OrderedFloat::from(text_height * scale),
};
match glyph.bitmap.as_ref() {
Some(bitmap_data) => {
match bitmap_data {
ImtBitmapData::Empty => continue,
ImtBitmapData::LRGBA(image_data) => {
self.basalt
.atlas_ref()
.load_image(
cache_id,
AtlasCacheCtrl::Indefinite,
Image::new(
ImageType::LRGBA,
ImageDims {
w: glyph.w,
h: glyph.h,
},
ImageData::D8(
image_data
.iter()
.map(|v| {
(*v * u8::max_value()
as f32)
.round()
as u8
})
.collect(),
),
)
.unwrap(),
)
.unwrap()
},
ImtBitmapData::Image(view) => {
self.basalt
.atlas_ref()
.load_image(
cache_id,
AtlasCacheCtrl::Indefinite,
Image::from_imt(view.clone()).unwrap(),
)
.unwrap()
},
}
},
None => continue,
}
},
}
};
let min_x = (glyph.x / scale) + pad_l + bps.tli[0];
let min_y = (glyph.y / scale) + pad_t + bps.tli[1];
let max_x = min_x + ((glyph.w as f32 - glyph.crop_x) / scale);
let max_y = min_y + ((glyph.h as f32 - glyph.crop_y) / scale);
let [c_min_x, c_min_y] = coords.top_left();
let [mut c_max_x, mut c_max_y] = coords.bottom_right();
c_max_x -= glyph.crop_x;
c_max_y -= glyph.crop_y;
let verts = text_state
.verts
.entry(coords.image_id())
.or_insert_with(Vec::new);
text_state.atlas_coords_in_use.insert(coords);
verts.push(ItfVertInfo {
position: [max_x, min_y, content_z],
coords: [c_max_x, c_min_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [min_x, min_y, content_z],
coords: [c_min_x, c_min_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [min_x, max_y, content_z],
coords: [c_min_x, c_max_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [max_x, min_y, content_z],
coords: [c_max_x, c_min_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [min_x, max_y, content_z],
coords: [c_min_x, c_max_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
verts.push(ItfVertInfo {
position: [max_x, max_y, content_z],
coords: [c_max_x, c_max_y],
color: color.as_array(),
ty: 2,
tex_i: 0,
});
text_state.glyphs.push(BinGlyphInfo {
min_x,
max_x,
min_y,
max_y,
});
}
}
for (img_id, verts) in text_state.verts.iter() {
vert_data.push((verts.clone(), None, *img_id));
}
bps.text_state = Some(text_state);
break;
}
}
if update_stats {
stats.t_text = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
for (verts, ..) in &mut vert_data {
for vert in verts {
if vert.position[1] < bps.unbound_mm_y[0] {
bps.unbound_mm_y[0] = vert.position[1];
}
if vert.position[1] > bps.unbound_mm_y[1] {
bps.unbound_mm_y[1] = vert.position[1];
}
if vert.position[0] < bps.unbound_mm_x[0] {
bps.unbound_mm_x[0] = vert.position[0];
}
if vert.position[0] > bps.unbound_mm_x[1] {
bps.unbound_mm_x[1] = vert.position[0];
}
}
}
let mut cut_amt;
let mut cut_percent;
let mut pos_min;
let mut pos_max;
let mut coords_min;
let mut coords_max;
let mut tri_dim;
let mut img_dim;
for (_check_bin, check_style, check_pft, check_pfl, check_w, check_h) in &ancestor_data {
let scroll_y = check_style.scroll_y.unwrap_or(0.0);
let scroll_x = check_style.scroll_x.unwrap_or(0.0);
let overflow_y = check_style.overflow_y.unwrap_or(false);
let overflow_x = check_style.overflow_x.unwrap_or(false);
let check_b = *check_pft + *check_h;
let check_r = *check_pfl + *check_w;
if !overflow_y {
let bps_check_y: Vec<&mut f32> = vec![
&mut bps.tli[1],
&mut bps.tri[1],
&mut bps.bli[1],
&mut bps.bri[1],
&mut bps.tlo[1],
&mut bps.tro[1],
&mut bps.blo[1],
&mut bps.bro[1],
];
for y in bps_check_y {
*y -= scroll_y;
if *y < *check_pft {
*y = *check_pft;
} else if *y > check_b {
*y = check_b;
}
}
}
if !overflow_x {
for x in [
&mut bps.tli[0],
&mut bps.tri[0],
&mut bps.bli[0],
&mut bps.bri[0],
&mut bps.tlo[0],
&mut bps.tro[0],
&mut bps.blo[0],
&mut bps.bro[0],
]
.into_iter()
{
*x -= scroll_x;
if *x < *check_pfl {
*x = *check_pfl;
} else if *x > check_r {
*x = check_r;
}
}
}
for (verts, ..) in &mut vert_data {
let mut rm_tris: Vec<usize> = Vec::new();
for (tri_i, tri) in verts.chunks_mut(3).enumerate() {
tri[0].position[1] -= scroll_y;
tri[1].position[1] -= scroll_y;
tri[2].position[1] -= scroll_y;
tri[0].position[0] -= scroll_x;
tri[1].position[0] -= scroll_x;
tri[2].position[0] -= scroll_x;
if !overflow_y {
if (tri[0].position[1] < *check_pft
&& tri[1].position[1] < *check_pft
&& tri[2].position[1] < *check_pft)
|| (tri[0].position[1] > check_b
&& tri[1].position[1] > check_b
&& tri[2].position[1] > check_b)
{
rm_tris.push(tri_i);
continue;
} else {
pos_min = tri[0].position[1]
.min(tri[1].position[1])
.min(tri[2].position[1]);
pos_max = tri[0].position[1]
.max(tri[1].position[1])
.max(tri[2].position[1]);
coords_min =
tri[0].coords[1].min(tri[1].coords[1]).min(tri[2].coords[1]);
coords_max =
tri[0].coords[1].max(tri[1].coords[1]).max(tri[2].coords[1]);
tri_dim = pos_max - pos_min;
img_dim = coords_max - coords_min;
for vert in tri.iter_mut() {
if vert.position[1] < *check_pft {
cut_amt = check_pft - vert.position[1];
cut_percent = cut_amt / tri_dim;
vert.coords[1] += cut_percent * img_dim;
vert.position[1] += cut_amt;
} else if vert.position[1] > check_b {
cut_amt = vert.position[1] - check_b;
cut_percent = cut_amt / tri_dim;
vert.coords[1] -= cut_percent * img_dim;
vert.position[1] -= cut_amt;
}
}
}
}
if !overflow_x {
if tri[0].position[0] < *check_pfl && tri[1].position[0] < *check_pfl
|| tri[0].position[0] > check_r
&& tri[1].position[0] > check_r
&& tri[2].position[0] > check_r
{
rm_tris.push(tri_i);
} else {
pos_min = tri[0].position[0]
.min(tri[1].position[0])
.min(tri[2].position[0]);
pos_max = tri[0].position[0]
.max(tri[1].position[0])
.max(tri[2].position[0]);
coords_min =
tri[0].coords[0].min(tri[1].coords[0]).min(tri[2].coords[0]);
coords_max =
tri[0].coords[0].max(tri[1].coords[0]).max(tri[2].coords[0]);
tri_dim = pos_max - pos_min;
img_dim = coords_max - coords_min;
for vert in tri.iter_mut() {
if vert.position[0] < *check_pfl {
cut_amt = check_pfl - vert.position[0];
cut_percent = cut_amt / tri_dim;
vert.coords[0] += cut_percent * img_dim;
vert.position[0] += cut_amt;
} else if vert.position[0] > check_r {
cut_amt = vert.position[0] - check_r;
cut_percent = cut_amt / tri_dim;
vert.coords[0] -= cut_percent * img_dim;
vert.position[0] -= cut_amt;
}
}
}
}
}
for tri_i in rm_tris.into_iter().rev() {
for i in (0..3).into_iter().rev() {
verts.swap_remove((tri_i * 3) + i);
}
}
}
}
if update_stats {
stats.t_overflow = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
for &mut (ref mut verts, ..) in &mut vert_data {
scale_verts(&[win_size[0], win_size[1]], scale, verts);
}
if update_stats {
stats.t_scale = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
*self.verts.lock() = VertexState {
verts: vert_data,
atlas_coords_in_use,
};
*self.post_update.write() = bps.clone();
*self.last_update.lock() = Instant::now();
if update_stats {
stats.t_locks = inst.elapsed();
stats.t_total += inst.elapsed();
inst = Instant::now();
}
let mut internal_hooks = self.internal_hooks.lock();
for hook_enum in internal_hooks
.get_mut(&InternalHookTy::Updated)
.unwrap()
.iter_mut()
{
if let InternalHookFn::Updated(func) = hook_enum {
func(self, &bps);
}
}
for hook_enum in internal_hooks
.get_mut(&InternalHookTy::UpdatedOnce)
.unwrap()
.drain(..)
{
if let InternalHookFn::Updated(mut func) = hook_enum {
func(self, &bps);
}
}
if update_stats {
stats.t_callbacks = inst.elapsed();
stats.t_total += inst.elapsed();
} else {
stats.t_total = inst.elapsed();
}
*self.update_stats.lock() = stats;
}
pub fn force_update(&self) {
self.update.store(true, atomic::Ordering::SeqCst);
self.basalt.interface_ref().composer_ref().unpark();
}
pub fn force_recursive_update(self: &Arc<Self>) {
self.force_update();
self.children_recursive()
.into_iter()
.for_each(|child| child.force_update());
}
pub fn update_children(&self) {
self.update_children_priv(false);
}
fn update_children_priv(&self, update_self: bool) {
if update_self {
self.update.store(true, atomic::Ordering::SeqCst);
self.basalt.interface_ref().composer_ref().unpark();
}
for child in self.children().into_iter() {
child.update_children_priv(true);
}
}
pub fn style(&self) -> Arc<BinStyle> {
self.style.load().clone()
}
pub fn style_copy(&self) -> BinStyle {
self.style.load().as_ref().clone()
}
#[track_caller]
pub fn style_update(&self, copy: BinStyle) -> BinStyleValidation {
let validation = copy.validate(
self.basalt.interface_ref(),
self.hrchy.load().parent.is_some(),
);
if !validation.errors_present() {
self.style.store(Arc::new(copy));
*self.initial.lock() = false;
self.update.store(true, atomic::Ordering::SeqCst);
self.basalt.interface_ref().composer_ref().unpark();
}
validation
}
pub fn hidden(self: &Arc<Self>, to: Option<bool>) {
let mut copy = self.style_copy();
copy.hidden = to;
self.style_update(copy).expect_valid();
self.update_children();
}
}
fn curve_line_segments(
a: (f32, f32),
b: (f32, f32),
c: (f32, f32),
) -> Vec<((f32, f32), (f32, f32))> {
let mut len = 0.0;
let mut lpt = a;
let mut steps = 10;
for s in 1..=steps {
let t = s as f32 / steps as f32;
let npt = (
((1.0 - t).powi(2) * a.0) + (2.0 * (1.0 - t) * t * b.0) + (t.powi(2) * c.0),
((1.0 - t).powi(2) * a.1) + (2.0 * (1.0 - t) * t * b.1) + (t.powi(2) * c.1),
);
len += ((lpt.0 - npt.0) + (lpt.1 - npt.1)).sqrt();
lpt = npt;
}
steps = len.ceil() as usize;
if steps < 3 {
steps = 3;
}
lpt = a;
let mut out = Vec::new();
for s in 1..=steps {
let t = s as f32 / steps as f32;
let npt = (
((1.0 - t).powi(2) * a.0) + (2.0 * (1.0 - t) * t * b.0) + (t.powi(2) * c.0),
((1.0 - t).powi(2) * a.1) + (2.0 * (1.0 - t) * t * b.1) + (t.powi(2) * c.1),
);
out.push(((lpt.0, lpt.1), (npt.0, npt.1)));
lpt = npt;
}
out
}