Skip to content

Commit

Permalink
Add arrow tool that can move single points
Browse files Browse the repository at this point in the history
Moving contours not yet implemented. Have to figure out double clicks
with `winit`.

Close #6
  • Loading branch information
ctrlcctrlv committed Sep 17, 2020
1 parent 642e30d commit 27e2f23
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 114 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ Qglif is the premier program of the Modular Font Editor Q project. This project

To make this as easy as possible to build, and cross-platform without hassle, the icon is compiled right into the binary via the Rust `include_str!` macro.

## Building

### Mac users

Apple charges a fee to "notarize" applications and without this "notarization" Qglif will not run correctly, or in some cases, at all. So, for the foreseeable future, you must _build Qglif from source on OS X_. This is not as hard as it sounds! :-)

* Download and install the [Vulkan SDK](https://vulkan.lunarg.com/).

### For everyone

* Download and install [`rustup`](https://rustup.rs/), selecting the `nightly` toolchain.
* Pull this repository, and finally
* Run the below command to get started.

### Errors?

If you previously pulled the repository and get errors related to `glifparser`, `mfeq-ipc`, or another local unstable dependency, try running `cargo update` to force Cargo to pull the latest versions from GitHub.

## Contributing

I typically build and run Qglif like this:
Expand Down
9 changes: 8 additions & 1 deletion src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ pub mod prelude;
use self::prelude::*;

pub mod console;

pub mod pan;
pub mod pen;
pub mod select;
pub mod zoom;

pub use self::zoom::{zoom_in_factor, zoom_out_factor};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MouseMeta {
pub modifiers: ModifiersState,
pub button: MouseButton,
}

// Generic events
pub fn center_cursor(winit_window: &Window) -> Result<(), winit::error::ExternalError> {
let mut center = winit_window.outer_size();
Expand Down Expand Up @@ -66,7 +73,7 @@ pub fn update_mousepos<T>(

pub fn mode_switched(from: Mode, to: Mode) {
assert!(from != to);
PEN_DATA.with(|v| v.borrow_mut().contour = None);
TOOL_DATA.with(|v| v.borrow_mut().contour = None);
}

#[macro_export]
Expand Down
36 changes: 5 additions & 31 deletions src/events/pen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,13 @@ use super::prelude::*;

use glifparser::{self, Contour, Handle, Outline, Point, PointType};

// $e of type RefCell<State<T>>
macro_rules! get_outline_mut {
($e:expr) => {
$e.borrow_mut()
.glyph
.as_mut()
.unwrap()
.glif
.outline
.as_mut()
.unwrap()
};
}
macro_rules! get_outline {
($e:expr) => {
$e.borrow()
.glyph
.as_ref()
.unwrap()
.glif
.outline
.as_ref()
.unwrap()
};
}

pub fn mouse_moved(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<Option<PointData>>>,
) -> bool {
let mposition = update_mousepos(position, &v, false);

PEN_DATA.with(|vv| {
TOOL_DATA.with(|vv| {
let contour = vv.borrow().contour;
match contour {
Some(idx) => {
Expand Down Expand Up @@ -67,11 +41,11 @@ pub fn mouse_moved(
pub fn mouse_pressed(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<Option<PointData>>>,
button: MouseButton,
meta: MouseMeta,
) -> bool {
let mposition = v.borrow().mousepos;

PEN_DATA.with(|vv| {
TOOL_DATA.with(|vv| {
let contour = vv.borrow().contour;
match contour {
Some(idx) => {
Expand All @@ -97,11 +71,11 @@ pub fn mouse_pressed(
pub fn mouse_released(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<Option<PointData>>>,
button: MouseButton,
meta: MouseMeta,
) -> bool {
let mposition = v.borrow().mousepos;

PEN_DATA.with(|vv| {
TOOL_DATA.with(|vv| {
//vv.borrow_mut().contour = None;
if let Some(idx) = vv.borrow().contour {
get_outline_mut!(v)[idx].last_mut().map(|point| {
Expand Down
12 changes: 8 additions & 4 deletions src/events/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// Our stuff
pub use super::MouseMeta;
pub use super::{center_cursor, mode_switched, update_mousepos, update_viewport};

pub use crate::renderer::constants::*;
pub use crate::renderer::points::calc::*;
pub use crate::state;
pub use crate::{CONSOLE, PEN_DATA, STATE};
pub use state::{Mode, PenData, PointData};
pub use crate::util;
pub use crate::{CONSOLE, STATE, TOOL_DATA};
pub use state::{Mode, PointData, ToolData};

// Skia/Winit stuff
pub use skulpin::skia_safe::{Canvas, Matrix};
pub use skulpin::skia_safe::Contains as _;
pub use skulpin::skia_safe::{Canvas, Matrix, Point as SkPoint, Rect as SkRect};
pub use skulpin::winit;
pub use skulpin::winit::dpi::{PhysicalPosition, PhysicalSize};
pub use skulpin::winit::event::MouseButton;
pub use skulpin::winit::event::{ModifiersState, MouseButton};
pub use skulpin::winit::window::Window;

// std
Expand Down
202 changes: 189 additions & 13 deletions src/events/select.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,215 @@
// Select
use super::prelude::*;
use crate::state::Follow;
use glifparser::{Handle, WhichHandle};

/// Get indexes stored by clicked_point_or_handle and move the points they refer to around.
pub fn mouse_moved<T>(position: PhysicalPosition<f64>, v: &RefCell<state::State<T>>) -> bool {
let mposition = update_mousepos(position, &v, false);
v.borrow_mut().corner_two = Some(mposition);
v.borrow().show_sel_box
if !v.borrow().mousedown {
return false;
}

let x = calc_x(mposition.x as f32);
let y = calc_y(mposition.y as f32);
let follow = TOOL_DATA.with(|p| p.borrow().follow);
let contour = TOOL_DATA.with(|p| p.borrow().contour);
let cur_point = TOOL_DATA.with(|p| p.borrow().cur_point);
let which_handle = TOOL_DATA.with(|p| p.borrow().handle);

let single_point = match (contour, cur_point, which_handle) {
// Point itself is being moved.
(Some(ci), Some(pi), WhichHandle::Neither) => {
let (cx, cy) = (get_outline!(v)[ci][pi].x, get_outline!(v)[ci][pi].y);
let (dx, dy) = (cx - x, cy - y);

get_outline_mut!(v)[ci][pi].x = x;
get_outline_mut!(v)[ci][pi].y = y;

match follow {
// ForceLine makes no sense in this context, but putting it here prevents us from
// falling back to the No branch.
Follow::Mirror | Follow::ForceLine => {
let a = get_outline!(v)[ci][pi].a;
let b = get_outline!(v)[ci][pi].b;
match a {
Handle::At(hx, hy) => {
get_outline_mut!(v)[ci][pi].a = Handle::At(hx - dx, hy - dy)
}
Handle::Colocated => (),
}
match b {
Handle::At(hx, hy) => {
get_outline_mut!(v)[ci][pi].b = Handle::At(hx - dx, hy - dy)
}
Handle::Colocated => (),
}
}
_ => (),
}

true
}
// A control point (A or B) is being moved.
(Some(ci), Some(pi), wh) => {
let handle = match wh {
WhichHandle::A => get_outline!(v)[ci][pi].a,
WhichHandle::B => get_outline!(v)[ci][pi].b,
WhichHandle::Neither => unreachable!("Should've been matched by above?!"),
};

// Current x, current y
let (cx, cy) = match handle {
Handle::At(cx, cy) => (cx, cy),
_ => panic!("Clicked non-existent handle A! Cidx {} pidx {}", ci, pi),
};

// Difference in x, difference in y
let (dx, dy) = (cx - x, cy - y);

// If Follow::Mirror (left mouse button), other control point (handle) will do mirror
// image action of currently selected control point. Perhaps pivoting around central
// point is better?
macro_rules! move_mirror {
($cur:ident, $mirror:ident) => {
get_outline_mut!(v)[ci][pi].$cur = Handle::At(x, y);
let h = get_outline!(v)[ci][pi].$mirror;
match h {
Handle::At(hx, hy) => {
if follow == Follow::Mirror {
get_outline_mut!(v)[ci][pi].$mirror = Handle::At(hx + dx, hy + dy);
} else if follow == Follow::ForceLine {
let (cx, cy) =
(get_outline!(v)[ci][pi].x, get_outline!(v)[ci][pi].y);
let (dx, dy) = (cx - x, cy - y);
get_outline_mut!(v)[ci][pi].$mirror = Handle::At(cx + dx, cy + dy);
}
}
Handle::Colocated => (),
}
};
}

#[rustfmt::skip]
match wh {
WhichHandle::A => { move_mirror!(a, b); },
WhichHandle::B => { move_mirror!(b, a); },
WhichHandle::Neither => unreachable!("Should've been matched by above?!"),
}

true
}
_ => false,
};

if !single_point {
v.borrow_mut().corner_two = Some(mposition);
}
true
}

// Placeholder
pub fn mouse_button<T>(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<T>>,
button: MouseButton,
meta: MouseMeta,
) -> bool {
false
}

pub fn mouse_pressed<T>(
/// Transform mouse click position into indexes into STATE.glyph.glif.outline and the handle if
/// applicable, and store it in TOOL_DATA.
fn clicked_point_or_handle(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<T>>,
button: MouseButton,
v: &RefCell<state::State<Option<state::PointData>>>,
) -> Option<(usize, usize, WhichHandle)> {
let factor = v.borrow().factor;
let mposition = update_mousepos(position, &v, true);
let mut contour_idx = 0;
let mut point_idx = 0;

// How we do this is quite naïve. For each click, we just iterate all points and check the
// point and both handles. It's just a bunch of floating point comparisons in a compiled
// language, so I'm not too concerned about it, and even in the TT2020 case doesn't seem to
// slow anything down.
for (contour_idx, contour) in get_outline!(v).iter().enumerate() {
for (point_idx, point) in contour.iter().enumerate() {
let size = ((POINT_RADIUS * 2.) + (POINT_STROKE_THICKNESS * 2.)) * (1. / factor);
// Topleft corner of point
let point_tl = SkPoint::new(
calc_x(point.x as f32) - (size / 2.),
calc_y(point.y as f32) - (size / 2.),
);
let point_rect = SkRect::from_point_and_size(point_tl, (size, size));
// Topleft corner of handle a
let a = point.handle_or_colocated(WhichHandle::A, |f| f, |f| f);
let a_tl = SkPoint::new(calc_x(a.0) - (size / 2.), calc_y(a.1) - (size / 2.));
let a_rect = SkRect::from_point_and_size(a_tl, (size, size));
// Topleft corner of handle b
let b = point.handle_or_colocated(WhichHandle::B, |f| f, |f| f);
let b_tl = SkPoint::new(calc_x(b.0) - (size / 2.), calc_y(b.1) - (size / 2.));
let b_rect = SkRect::from_point_and_size(b_tl, (size, size));

// winit::PhysicalPosition as an SkPoint
let sk_mpos = SkPoint::new(mposition.x as f32, mposition.y as f32);

if point_rect.contains(sk_mpos) {
return Some((contour_idx, point_idx, WhichHandle::Neither));
} else if a_rect.contains(sk_mpos) {
return Some((contour_idx, point_idx, WhichHandle::A));
} else if b_rect.contains(sk_mpos) {
return Some((contour_idx, point_idx, WhichHandle::B));
}
}
}
None
}

pub fn mouse_pressed(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<Option<state::PointData>>>,
meta: MouseMeta,
) -> bool {
v.borrow_mut().show_sel_box = true;
let position = v.borrow().mousepos;
let mposition = PhysicalPosition::from((position.x, position.y));
v.borrow_mut().mousepos = mposition;
if v.borrow().show_sel_box {
v.borrow_mut().corner_one = Some(mposition);
let single_point = match clicked_point_or_handle(position, v) {
Some((ci, pi, wh)) => TOOL_DATA.with(|p| {
let follow: Follow = meta.into();
debug!(
"Clicked point: {:?} {:?}. Follow behavior: {}",
get_outline!(v)[ci][pi],
wh,
follow
);
p.borrow_mut().contour = Some(ci);
p.borrow_mut().cur_point = Some(pi);
p.borrow_mut().follow = follow;
p.borrow_mut().handle = wh;
true
}),
None => TOOL_DATA.with(|p| {
p.borrow_mut().contour = None;
p.borrow_mut().cur_point = None;
p.borrow_mut().handle = WhichHandle::Neither;
false
}),
};

if !single_point {
v.borrow_mut().show_sel_box = true;
let position = v.borrow().mousepos;
let mposition = PhysicalPosition::from((position.x, position.y));
v.borrow_mut().mousepos = mposition;
if v.borrow().show_sel_box {
v.borrow_mut().corner_one = Some(mposition);
v.borrow_mut().corner_two = Some(mposition);
}
}
false
}

pub fn mouse_released<T>(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<T>>,
button: MouseButton,
meta: MouseMeta,
) -> bool {
v.borrow_mut().show_sel_box = false;
true
Expand Down
4 changes: 2 additions & 2 deletions src/events/zoom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pub fn mouse_moved<T>(position: PhysicalPosition<f64>, v: &RefCell<state::State<
pub fn mouse_released<T>(
position: PhysicalPosition<f64>,
v: &RefCell<state::State<T>>,
button: MouseButton,
meta: MouseMeta,
) -> bool {
let mut scale = v.borrow().factor;
match button {
match meta.button {
MouseButton::Left => {
scale = zoom_in_factor(scale, &v);
}
Expand Down
Loading

0 comments on commit 27e2f23

Please sign in to comment.