Rotate and scale around the appropriate points in the demo

This commit is contained in:
Patrick Walton 2019-02-13 16:05:28 -08:00
parent 4053b7dd0c
commit 0983812b90
4 changed files with 123 additions and 23 deletions

View File

@ -191,13 +191,7 @@ impl DemoApp {
RenderTransform::Perspective(Perspective::new(&transform, &drawable_size)) RenderTransform::Perspective(Perspective::new(&transform, &drawable_size))
} }
Camera::TwoD { ref position, scale } => { Camera::TwoD(transform) => RenderTransform::Transform2D(transform),
let mut transform = Transform2DF32::from_rotation(self.ui.rotation());
transform =
transform.post_mul(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
transform = transform.post_mul(&Transform2DF32::from_translation(position));
RenderTransform::Transform2D(transform)
}
}; };
let count = if self.frame_counter == 0 { 2 } else { 1 }; let count = if self.frame_counter == 0 { 2 } else { 1 };
@ -263,8 +257,14 @@ impl DemoApp {
self.dirty = true; self.dirty = true;
} }
Event::MultiGesture { d_dist, .. } => { Event::MultiGesture { d_dist, .. } => {
if let Camera::TwoD { ref mut scale, .. } = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*scale *= 1.0 + d_dist * CAMERA_SCALE_SPEED_2D; let mouse_state = self.sdl_event_pump.mouse_state();
let position = Point2DI32::new(mouse_state.x(), mouse_state.y());
let position = position.to_f32().scale(self.scale_factor);
*transform = transform.post_translate(-position);
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
*transform = transform.post_scale(Point2DF32::splat(scale_delta));
*transform = transform.post_translate(position);
} }
} }
Event::KeyDown { keycode: Some(Keycode::W), .. } => { Event::KeyDown { keycode: Some(Keycode::W), .. } => {
@ -358,17 +358,34 @@ impl DemoApp {
self.dirty = true; self.dirty = true;
} }
UIAction::ZoomIn => { UIAction::ZoomIn => {
if let Camera::TwoD { ref mut scale, .. } = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*scale *= 1.0 + CAMERA_ZOOM_AMOUNT_2D; let scale = Point2DF32::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window);
*transform = transform.post_translate(-center)
.post_scale(scale)
.post_translate(center);
self.dirty = true; self.dirty = true;
} }
} }
UIAction::ZoomOut => { UIAction::ZoomOut => {
if let Camera::TwoD { ref mut scale, .. } = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*scale *= 1.0 - CAMERA_ZOOM_AMOUNT_2D; let scale = Point2DF32::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window);
*transform = transform.post_translate(-center)
.post_scale(scale)
.post_translate(center);
self.dirty = true; self.dirty = true;
} }
} }
UIAction::Rotate(theta) => {
if let Camera::TwoD(ref mut transform) = self.camera {
let old_rotation = transform.rotation();
let center = center_of_window(&self.window);
*transform = transform.post_translate(-center)
.post_rotate(theta - old_rotation)
.post_translate(center);
}
}
} }
// Switch camera mode (2D/3D) if requested. // Switch camera mode (2D/3D) if requested.
@ -386,8 +403,8 @@ impl DemoApp {
self.mouselook_enabled = !self.mouselook_enabled; self.mouselook_enabled = !self.mouselook_enabled;
} }
UIEvent::MouseDragged { relative_position, .. } => { UIEvent::MouseDragged { relative_position, .. } => {
if let Camera::TwoD { ref mut position, .. } = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*position = *position + relative_position.to_f32(); *transform = transform.post_translate(relative_position.to_f32());
} }
} }
_ => {} _ => {}
@ -574,14 +591,19 @@ fn update_drawable_size(window: &Window, scene_thread_proxy: &SceneThreadProxy)
drawable_size drawable_size
} }
fn center_of_window(window: &Window) -> Point2DF32 {
let (drawable_width, drawable_height) = window.drawable_size();
Point2DI32::new(drawable_width as i32, drawable_height as i32).to_f32().scale(0.5)
}
enum Camera { enum Camera {
TwoD { position: Point2DF32, scale: f32 }, TwoD(Transform2DF32),
ThreeD { position: Point3DF32, velocity: Point3DF32, yaw: f32, pitch: f32 }, ThreeD { position: Point3DF32, velocity: Point3DF32, yaw: f32, pitch: f32 },
} }
impl Camera { impl Camera {
fn two_d() -> Camera { fn two_d() -> Camera {
Camera::TwoD { position: Point2DF32::new(0.0, 0.0), scale: 1.0 } Camera::TwoD(Transform2DF32::default())
} }
fn three_d() -> Camera { fn three_d() -> Camera {

View File

@ -81,7 +81,7 @@ impl DemoUI {
} }
} }
pub fn rotation(&self) -> f32 { fn rotation(&self) -> f32 {
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI (self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
} }
@ -145,7 +145,7 @@ impl DemoUI {
self.draw_effects_panel(debug_ui, event); self.draw_effects_panel(debug_ui, event);
// Draw rotate panel, if necessary. // Draw rotate panel, if necessary.
self.draw_rotate_panel(debug_ui, event); self.draw_rotate_panel(debug_ui, event, action);
} }
fn draw_effects_panel(&mut self, debug_ui: &mut DebugUI, event: &mut UIEvent) { fn draw_effects_panel(&mut self, debug_ui: &mut DebugUI, event: &mut UIEvent) {
@ -184,7 +184,10 @@ impl DemoUI {
} }
fn draw_rotate_panel(&mut self, debug_ui: &mut DebugUI, event: &mut UIEvent) { fn draw_rotate_panel(&mut self,
debug_ui: &mut DebugUI,
event: &mut UIEvent,
action: &mut UIAction) {
if !self.rotate_panel_visible { if !self.rotate_panel_visible {
return; return;
} }
@ -201,6 +204,7 @@ impl DemoUI {
Point2DI32::new(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT)); Point2DI32::new(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT));
if let Some(position) = event.handle_mouse_down_or_dragged_in_rect(widget_rect) { if let Some(position) = event.handle_mouse_down_or_dragged_in_rect(widget_rect) {
self.rotation = position.x(); self.rotation = position.x();
*action = UIAction::Rotate(self.rotation());
} }
let slider_track_y = rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 - let slider_track_y = rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 -
@ -291,6 +295,7 @@ pub enum UIAction {
OpenFile(PathBuf), OpenFile(PathBuf),
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
Rotate(f32),
} }
pub enum UIEvent { pub enum UIEvent {

View File

@ -12,7 +12,7 @@
use euclid::Point2D; use euclid::Point2D;
use pathfinder_simd::default::{F32x4, I32x4}; use pathfinder_simd::default::{F32x4, I32x4};
use std::ops::{Add, AddAssign, Mul, Sub}; use std::ops::{Add, AddAssign, Mul, Neg, Sub};
/// 2D points with 32-bit floating point coordinates. /// 2D points with 32-bit floating point coordinates.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
@ -161,6 +161,14 @@ impl Mul<Point2DF32> for Point2DF32 {
} }
} }
impl Neg for Point2DF32 {
type Output = Point2DF32;
#[inline]
fn neg(self) -> Point2DF32 {
Point2DF32(-self.0)
}
}
/// 2D points with 32-bit signed integer coordinates. /// 2D points with 32-bit signed integer coordinates.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct Point2DI32(pub I32x4); pub struct Point2DI32(pub I32x4);

View File

@ -82,6 +82,15 @@ impl Matrix2x2F32 {
pub fn inverse(&self) -> Matrix2x2F32 { pub fn inverse(&self) -> Matrix2x2F32 {
Matrix2x2F32(F32x4::splat(1.0 / self.det()) * self.adjugate().0) Matrix2x2F32(F32x4::splat(1.0 / self.det()) * self.adjugate().0)
} }
#[inline]
pub fn m11(&self) -> f32 { self.0[0] }
#[inline]
pub fn m21(&self) -> f32 { self.0[1] }
#[inline]
pub fn m12(&self) -> f32 { self.0[2] }
#[inline]
pub fn m22(&self) -> f32 { self.0[3] }
} }
impl Sub<Matrix2x2F32> for Matrix2x2F32 { impl Sub<Matrix2x2F32> for Matrix2x2F32 {
@ -132,6 +141,14 @@ impl Transform2DF32 {
} }
} }
#[inline]
pub fn from_scale_rotation_translation(scale: Point2DF32, theta: f32, translation: Point2DF32)
-> Transform2DF32 {
let rotation = Transform2DF32::from_rotation(theta);
let translation = Transform2DF32::from_translation(&translation);
Transform2DF32::from_scale(&scale).post_mul(&rotation).post_mul(&translation)
}
#[inline] #[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32)
-> Transform2DF32 { -> Transform2DF32 {
@ -182,6 +199,54 @@ impl Transform2DF32 {
pub fn is_identity(&self) -> bool { pub fn is_identity(&self) -> bool {
*self == Transform2DF32::default() *self == Transform2DF32::default()
} }
#[inline]
pub fn m11(&self) -> f32 { self.matrix.m11() }
#[inline]
pub fn m21(&self) -> f32 { self.matrix.m21() }
#[inline]
pub fn m12(&self) -> f32 { self.matrix.m12() }
#[inline]
pub fn m22(&self) -> f32 { self.matrix.m22() }
#[inline]
pub fn post_translate(&self, vector: Point2DF32) -> Transform2DF32 {
self.post_mul(&Transform2DF32::from_translation(&vector))
}
#[inline]
pub fn post_rotate(&self, theta: f32) -> Transform2DF32 {
self.post_mul(&Transform2DF32::from_rotation(theta))
}
#[inline]
pub fn post_scale(&self, scale: Point2DF32) -> Transform2DF32 {
self.post_mul(&Transform2DF32::from_scale(&scale))
}
/// Returns the translation part of this matrix.
///
/// This decomposition assumes that scale, rotation, and translation are applied in that order.
#[inline]
pub fn translation(&self) -> Point2DF32 {
self.vector
}
/// Returns the rotation angle of this matrix.
///
/// This decomposition assumes that scale, rotation, and translation are applied in that order.
#[inline]
pub fn rotation(&self) -> f32 {
f32::atan2(self.m21(), self.m11())
}
/// Returns the scale factor of this matrix.
///
/// This decomposition assumes that scale, rotation, and translation are applied in that order.
#[inline]
pub fn scale_factor(&self) -> f32 {
Point2DF32(self.matrix.0.zwxy()).length()
}
} }
/// Transforms a path with a SIMD 2D transform. /// Transforms a path with a SIMD 2D transform.