#![warn(missing_docs)]
use fidget_core::render::{ImageSize, VoxelSize};
use nalgebra::{
Const, DefaultAllocator, DimNameAdd, DimNameSum, Matrix3, Matrix4, OMatrix,
OPoint, OVector, Point2, Point3, U1, Vector2, Vector3,
allocator::Allocator,
};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct View2 {
center: Vector2<f32>,
scale: f32,
}
impl Default for View2 {
fn default() -> Self {
Self {
scale: 1.0,
center: Vector2::new(0.0, 0.0),
}
}
}
impl View2 {
pub fn from_center_and_scale(center: Vector2<f32>, scale: f32) -> Self {
Self { center, scale }
}
pub fn components(&self) -> (Vector2<f32>, f32) {
(self.center, self.scale)
}
pub fn from_components(center: Vector2<f32>, scale: f32) -> Self {
Self::from_center_and_scale(center, scale)
}
fn scale_mat(&self) -> Matrix3<f32> {
Matrix3::new_scaling(self.scale)
}
fn translation_mat(&self) -> Matrix3<f32> {
Matrix3::new_translation(&self.center)
}
pub fn world_to_model(&self) -> Matrix3<f32> {
self.translation_mat() * self.scale_mat()
}
pub fn transform_point(&self, p: &Point2<f32>) -> Point2<f32> {
self.world_to_model().transform_point(p)
}
pub fn begin_translate(&self, start: Point2<f32>) -> TranslateHandle<2> {
let initial_mat = self.world_to_model();
TranslateHandle {
start: initial_mat.transform_point(&start),
initial_mat,
initial_center: self.center,
}
}
pub fn translate(
&mut self,
h: &TranslateHandle<2>,
pos: Point2<f32>,
) -> bool {
let next_center = h.center(pos);
let changed = next_center != self.center;
self.center = next_center;
changed
}
pub fn zoom(&mut self, amount: f32, pos: Option<Point2<f32>>) -> bool {
match pos {
Some(before) => {
let pos_before = self.transform_point(&before);
self.scale *= amount;
let pos_after = self.transform_point(&before);
self.center += pos_before - pos_after;
}
None => {
self.scale *= amount;
}
}
amount != 1.0
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct View3 {
center: Vector3<f32>,
scale: f32,
yaw: f32,
pitch: f32,
}
impl Default for View3 {
fn default() -> Self {
Self {
center: Vector3::new(0.0, 0.0, 0.0),
scale: 1.0,
yaw: 0.0,
pitch: 0.0,
}
}
}
impl View3 {
pub fn from_center_and_scale(center: Vector3<f32>, scale: f32) -> Self {
Self {
center,
scale,
yaw: 0.0,
pitch: 0.0,
}
}
pub fn components(&self) -> (Vector3<f32>, f32, f32, f32) {
(self.center, self.scale, self.yaw, self.pitch)
}
pub fn from_components(
center: Vector3<f32>,
scale: f32,
yaw: f32,
pitch: f32,
) -> Self {
Self {
center,
scale,
yaw,
pitch,
}
}
pub fn world_to_model(&self) -> Matrix4<f32> {
self.translation_mat() * self.rot_mat() * self.scale_mat()
}
pub fn transform_point(&self, p: &Point3<f32>) -> Point3<f32> {
self.world_to_model().transform_point(p)
}
pub fn begin_translate(&self, start: Point3<f32>) -> TranslateHandle<3> {
let initial_mat = self.world_to_model();
TranslateHandle {
start: initial_mat.transform_point(&start),
initial_mat,
initial_center: self.center,
}
}
fn scale_mat(&self) -> Matrix4<f32> {
Matrix4::new_scaling(self.scale)
}
fn rot_mat(&self) -> Matrix4<f32> {
Matrix4::from_axis_angle(
&nalgebra::Unit::new_normalize(Vector3::new(0.0, 0.0, 1.0)),
self.yaw,
) * Matrix4::from_axis_angle(
&nalgebra::Unit::new_normalize(Vector3::new(1.0, 0.0, 0.0)),
self.pitch,
)
}
fn translation_mat(&self) -> Matrix4<f32> {
Matrix4::new_translation(&self.center)
}
pub fn translate(
&mut self,
h: &TranslateHandle<3>,
pos: Point3<f32>,
) -> bool {
let next_center = h.center(pos);
let changed = next_center != self.center;
self.center = next_center;
changed
}
pub fn zoom(&mut self, amount: f32, pos: Option<Point3<f32>>) -> bool {
match pos {
Some(before) => {
let pos_before = self.transform_point(&before);
self.scale *= amount;
let pos_after = self.transform_point(&before);
self.center += pos_before - pos_after;
}
None => {
self.scale *= amount;
}
}
amount != 1.0
}
pub fn begin_rotate(&self, start: Point3<f32>) -> RotateHandle {
RotateHandle {
start,
initial_yaw: self.yaw,
initial_pitch: self.pitch,
}
}
pub fn rotate(&mut self, h: &RotateHandle, pos: Point3<f32>) -> bool {
let next_yaw = h.yaw(pos.x);
let next_pitch = h.pitch(pos.y);
let changed = (next_yaw != self.yaw) || (next_pitch != self.pitch);
self.yaw = next_yaw;
self.pitch = next_pitch;
changed
}
}
#[derive(Copy, Clone)]
pub struct RotateHandle {
start: Point3<f32>,
initial_yaw: f32,
initial_pitch: f32,
}
const ROTATE_SPEED: f32 = 2.0;
impl RotateHandle {
fn yaw(&self, x: f32) -> f32 {
(self.initial_yaw + (self.start.x - x) * ROTATE_SPEED)
% std::f32::consts::TAU
}
fn pitch(&self, y: f32) -> f32 {
(self.initial_pitch + (y - self.start.y) * ROTATE_SPEED)
.clamp(0.0, std::f32::consts::PI)
}
}
#[derive(Copy, Clone)]
pub struct TranslateHandle<const N: usize>
where
Const<N>: DimNameAdd<U1>,
DefaultAllocator:
Allocator<DimNameSum<Const<N>, U1>, DimNameSum<Const<N>, U1>>,
OMatrix<
f32,
<Const<N> as DimNameAdd<Const<1>>>::Output,
<Const<N> as DimNameAdd<Const<1>>>::Output,
>: Copy,
{
start: OPoint<f32, Const<N>>,
initial_mat: OMatrix<
f32,
<Const<N> as DimNameAdd<Const<1>>>::Output,
<Const<N> as DimNameAdd<Const<1>>>::Output,
>,
initial_center: OVector<f32, Const<N>>,
}
impl TranslateHandle<2> {
fn center(&self, pos: Point2<f32>) -> Vector2<f32> {
let pos_model = self.initial_mat.transform_point(&pos);
self.initial_center - (pos_model - self.start)
}
}
impl TranslateHandle<3> {
fn center(&self, pos: Point3<f32>) -> Vector3<f32> {
let pos_model = self.initial_mat.transform_point(&pos);
self.initial_center - (pos_model - self.start)
}
}
#[derive(Copy, Clone)]
pub struct Canvas2 {
view: View2,
image_size: ImageSize,
drag_start: Option<TranslateHandle<2>>,
}
#[derive(Copy, Clone, Debug)]
pub struct CursorState<D> {
pub screen_pos: Point2<i32>,
pub drag: D,
}
impl Canvas2 {
pub fn new(image_size: ImageSize) -> Self {
Self {
view: View2::default(),
image_size,
drag_start: None,
}
}
pub fn components(&self) -> (View2, ImageSize) {
(self.view, self.image_size)
}
pub fn from_components(view: View2, image_size: ImageSize) -> Self {
Self {
view,
image_size,
drag_start: None,
}
}
#[must_use]
pub fn interact(
&mut self,
image_size: ImageSize,
cursor_state: Option<CursorState<bool>>,
scroll: f32,
) -> bool {
self.image_size = image_size;
let mut changed = false;
let pos_screen = match cursor_state {
Some(cs) => {
if cs.drag {
self.begin_drag(cs.screen_pos); changed |= self.drag(cs.screen_pos);
} else {
self.end_drag();
}
Some(cs.screen_pos)
}
_ => {
self.end_drag();
None
}
};
changed |= self.zoom(scroll, pos_screen);
changed
}
pub fn view(&self) -> View2 {
self.view
}
pub fn image_size(&self) -> ImageSize {
self.image_size
}
pub fn resize(&mut self, image_size: ImageSize) {
self.image_size = image_size;
}
pub fn begin_drag(&mut self, pos_screen: Point2<i32>) {
if self.drag_start.is_none() {
let pos_world = self.image_size.transform_point(pos_screen);
self.drag_start = Some(self.view.begin_translate(pos_world));
}
}
#[must_use]
pub fn drag(&mut self, pos_screen: Point2<i32>) -> bool {
if let Some(prev) = &self.drag_start {
let pos_world = self.image_size.transform_point(pos_screen);
self.view.translate(prev, pos_world)
} else {
false
}
}
pub fn end_drag(&mut self) {
self.drag_start = None;
}
#[must_use]
pub fn zoom(
&mut self,
amount: f32,
pos_screen: Option<Point2<i32>>,
) -> bool {
let pos_world = pos_screen.map(|p| self.image_size.transform_point(p));
self.view.zoom((amount / 100.0).exp2(), pos_world)
}
}
#[derive(Copy, Clone)]
pub struct Canvas3 {
view: View3,
image_size: VoxelSize,
drag_start: Option<Drag3>,
}
#[derive(Copy, Clone)]
pub enum DragMode {
Pan,
Rotate,
}
#[derive(Copy, Clone)]
enum Drag3 {
Pan(TranslateHandle<3>),
Rotate(RotateHandle),
}
#[allow(missing_docs)]
impl Canvas3 {
pub fn new(image_size: VoxelSize) -> Self {
Self {
view: View3::default(),
image_size,
drag_start: None,
}
}
pub fn components(&self) -> (View3, VoxelSize) {
(self.view, self.image_size)
}
pub fn from_components(view: View3, image_size: VoxelSize) -> Self {
Self {
view,
image_size,
drag_start: None,
}
}
pub fn image_size(&self) -> VoxelSize {
self.image_size
}
#[must_use]
pub fn interact(
&mut self,
image_size: VoxelSize,
cursor_state: Option<CursorState<Option<DragMode>>>,
scroll: f32,
) -> bool {
let mut changed = false;
self.image_size = image_size;
let pos_screen = match cursor_state {
Some(cs) => {
if let Some(drag_mode) = cs.drag {
self.begin_drag(cs.screen_pos, drag_mode); changed |= self.drag(cs.screen_pos);
} else {
self.end_drag();
}
Some(cs.screen_pos)
}
_ => {
self.end_drag();
None
}
};
changed |= self.zoom(scroll, pos_screen);
changed
}
pub fn view(&self) -> View3 {
self.view
}
pub fn begin_drag(&mut self, pos_screen: Point2<i32>, drag_mode: DragMode) {
if self.drag_start.is_none() {
let pos_world = self.screen_to_world(pos_screen);
self.drag_start = Some(match drag_mode {
DragMode::Pan => {
Drag3::Pan(self.view.begin_translate(pos_world))
}
DragMode::Rotate => {
Drag3::Rotate(self.view.begin_rotate(pos_world))
}
});
}
}
fn screen_to_world(&self, pos_screen: Point2<i32>) -> Point3<f32> {
self.image_size.transform_point(Point3::new(
pos_screen.x,
pos_screen.y,
0,
))
}
#[must_use]
pub fn drag(&mut self, pos_screen: Point2<i32>) -> bool {
let pos_world = self.screen_to_world(pos_screen);
match &self.drag_start {
Some(Drag3::Pan(prev)) => self.view.translate(prev, pos_world),
Some(Drag3::Rotate(prev)) => self.view.rotate(prev, pos_world),
None => false,
}
}
pub fn end_drag(&mut self) {
self.drag_start = None;
}
#[must_use]
pub fn zoom(
&mut self,
amount: f32,
pos_screen: Option<Point2<i32>>,
) -> bool {
let pos_world = pos_screen.map(|p| self.screen_to_world(p));
self.view.zoom((amount / 100.0).exp2(), pos_world)
}
}