Skip to content

Commit

Permalink
Zoom controls
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Aug 31, 2024
1 parent 9b7aabd commit 0e04076
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 33 deletions.
85 changes: 70 additions & 15 deletions src/cushy/skeleton_canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ use core::f32;

use crate::{Angle, BoneEnd, BoneId, Coordinate, JointId, Skeleton, Vector};
use cushy::{
context::{EventContext, GraphicsContext, LayoutContext},
context::{EventContext, GraphicsContext, LayoutContext, Trackable},
figures::{
units::{Lp, Px, UPx},
FloatConversion, IntoComponents, Point, Rect, Round, ScreenScale, Size,
},
kludgine::{
app::winit::{event::MouseButton, window::CursorIcon},
app::winit::{
event::{MouseButton, MouseScrollDelta, TouchPhase},
window::CursorIcon,
},
shapes::{PathBuilder, Shape, StrokeOptions},
DrawableExt, Origin,
},
styles::Color,
value::{Dynamic, DynamicRead},
value::{Destination, Dynamic, DynamicRead, Source},
widget::{Callback, EventHandling, Widget, HANDLED, IGNORED},
window::DeviceId,
ConstraintLimit,
Expand All @@ -24,9 +27,10 @@ use cushy::{
pub struct SkeletonCanvas {
skeleton: Dynamic<Skeleton>,
hovering: Option<Target>,
scale: f32,
scale: Dynamic<f32>,
handle_size: f32,
maximum_scale: f32,
maximum_scale: Dynamic<f32>,
minimum_scale: Dynamic<f32>,
offset: Point<Px>,
drag: Option<DragInfo>,
on_mutate: Option<Callback<SkeletonMutation>>,
Expand All @@ -35,18 +39,36 @@ pub struct SkeletonCanvas {
impl SkeletonCanvas {
#[must_use]
pub fn new(skeleton: Dynamic<Skeleton>) -> Self {
let maximum_scale = Dynamic::new(0.);
let minimum_scale = maximum_scale.map_each_cloned(|s| s / 100.);
Self {
skeleton,
hovering: None,
handle_size: 0.1,
scale: f32::MAX,
maximum_scale: 0.,
scale: Dynamic::new(f32::MAX),
maximum_scale,
minimum_scale,
offset: Point::default(),
drag: None,
on_mutate: None,
}
}

#[must_use]
pub fn maximum_scale(&self) -> &Dynamic<f32> {
&self.maximum_scale
}

#[must_use]
pub fn minimum_scale(&self) -> &Dynamic<f32> {
&self.minimum_scale
}

#[must_use]
pub fn scale(&self) -> &Dynamic<f32> {
&self.scale
}

#[must_use]
pub fn on_mutate<F>(mut self, on_mutate: F) -> Self
where
Expand All @@ -57,14 +79,17 @@ impl SkeletonCanvas {
}

fn coordinate_to_point(&self, vector: Coordinate) -> Point<Px> {
(vector * self.scale).to_vec::<Point<f32>>().map(Px::from) + self.offset
(vector * self.scale.get())
.to_vec::<Point<f32>>()
.map(Px::from)
+ self.offset
}

fn point_to_coordinate(&self, position: Point<Px>) -> Coordinate {
(position - self.offset)
.map(FloatConversion::into_float)
.to_vec::<Coordinate>()
/ self.scale
/ self.scale.get()
}
}

Expand Down Expand Up @@ -103,18 +128,25 @@ impl Widget for SkeletonCanvas {
return;
}

self.maximum_scale = if zero_height || width_ratio < height_ratio {
let maximum_scale = if zero_height || width_ratio < height_ratio {
width_ratio
} else {
height_ratio
};
if self.scale > self.maximum_scale {
self.scale = self.maximum_scale;
}
self.maximum_scale.set(maximum_scale);
self.scale.redraw_when_changed(context);
let scale = {
let mut scale = self.scale.lock();
if *scale > maximum_scale {
*scale = maximum_scale;
scale.prevent_notifications();
}
*scale
};
let handle_size = Lp::mm(2).into_px(context.gfx.scale()).ceil();
self.handle_size = handle_size.into_float() / self.scale;
self.handle_size = handle_size.into_float() / scale;

let root = root_start * self.scale;
let root = root_start * scale;

self.offset = (middle - root).to_vec::<Point<f32>>().map(Px::from).floor();

Expand Down Expand Up @@ -329,6 +361,29 @@ impl Widget for SkeletonCanvas {
self.drag = None;
}

#[allow(clippy::cast_possible_truncation)]
fn mouse_wheel(
&mut self,
_device_id: DeviceId,
delta: MouseScrollDelta,
_phase: TouchPhase,
_context: &mut EventContext<'_>,
) -> EventHandling {
let maximum_scale = self.maximum_scale.get();
let minimum_scale = self.minimum_scale.get();
let mut scale = self.scale.lock();
let delta = match delta {
MouseScrollDelta::LineDelta(_, y_lines) => y_lines,
MouseScrollDelta::PixelDelta(pt) => pt.y as f32 / 12.,
};

*scale = (*scale + *scale * delta / 10.)
.min(maximum_scale)
.max(minimum_scale);

HANDLED
}

fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
true
}
Expand Down
37 changes: 19 additions & 18 deletions src/funnybones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ fn main() -> cushy::Result {
let bones_editor = skeleton_editor(&editing_skeleton, &watcher).make_widget();

let mode = Dynamic::<Mode>::default();
let canvas = SkeletonCanvas::new(skeleton).on_mutate({
move |mutation| match mutation {
SkeletonMutation::SetDesiredEnd { bone, end } => editing_skeleton
.find_bone(bone)
.expect("missing bone")
.desired_end
.set(Some(end)),
SkeletonMutation::SetJointRotation { joint, rotation } => editing_skeleton
.find_joint(joint)
.expect("missing joint")
.joint_angle
.set(rotation),
}
});
let zoom = canvas
.scale()
.clone()
.slider_between(canvas.minimum_scale(), canvas.maximum_scale());

[(Mode::Bones, "Bones"), (Mode::Animation, "Animation")]
.into_iter()
Expand All @@ -104,24 +122,7 @@ fn main() -> cushy::Result {
)
.into_rows()
.expand()
.and(
SkeletonCanvas::new(skeleton)
.on_mutate({
move |mutation| match mutation {
SkeletonMutation::SetDesiredEnd { bone, end } => editing_skeleton
.find_bone(bone)
.expect("missing bone")
.desired_end
.set(Some(end)),
SkeletonMutation::SetJointRotation { joint, rotation } => editing_skeleton
.find_joint(joint)
.expect("missing joint")
.joint_angle
.set(rotation),
}
})
.expand(),
)
.and(canvas.expand().and(zoom).into_rows().expand())
.into_columns()
.run()
}
Expand Down

0 comments on commit 0e04076

Please sign in to comment.