mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
Add Shape Tool for drawing polygons (#75)
* ⬠ Add polygon drawing tool * 🔤 Minor fix of variable and function names * ❌ Remove stroke * ⌨️ Use N key as polygo tool shortcut. * ⌨️ Now using key Y for polygons. * ⌨️ The tooltip for the shortcut is fixed
This commit is contained in:
committed by
Keavon Chambers
parent
0ca4b9fe7c
commit
90df412aab
@@ -1,6 +1,7 @@
|
||||
pub mod operation;
|
||||
|
||||
pub use kurbo::{Circle, Line, Point, Rect};
|
||||
mod shape_points;
|
||||
pub use kurbo::{Circle, Line, Point, Rect, Vec2};
|
||||
pub use operation::Operation;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -9,6 +10,7 @@ pub enum LayerType {
|
||||
Circle(Circle),
|
||||
Rect(Rect),
|
||||
Line(Line),
|
||||
Shape(shape_points::ShapePoints),
|
||||
}
|
||||
|
||||
impl LayerType {
|
||||
@@ -24,6 +26,9 @@ impl LayerType {
|
||||
Self::Line(l) => {
|
||||
format!(r#"<line x1="{}" y1="{}" x2="{}" y2="{}" style="stroke: #fff;" />"#, l.p0.x, l.p0.y, l.p1.x, l.p1.y)
|
||||
}
|
||||
Self::Shape(s) => {
|
||||
format!(r#"<polygon points="{}" style="fill: #fff;" />"#, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,6 +225,20 @@ impl Document {
|
||||
|
||||
update_frontend(self.render());
|
||||
}
|
||||
Operation::AddShape {
|
||||
path,
|
||||
insert_index,
|
||||
x0,
|
||||
y0,
|
||||
x1,
|
||||
y1,
|
||||
sides,
|
||||
} => {
|
||||
let s = shape_points::ShapePoints::new(Point::new(x0, y0), Vec2 { x: x0 - x1, y: y0 - y1 }, sides);
|
||||
self.add_layer(&path, Layer::new(LayerType::Shape(s)), insert_index)?;
|
||||
|
||||
update_frontend(self.render());
|
||||
}
|
||||
Operation::DeleteLayer { path } => {
|
||||
self.delete(&path)?;
|
||||
|
||||
|
||||
@@ -24,6 +24,15 @@ pub enum Operation {
|
||||
x1: f64,
|
||||
y1: f64,
|
||||
},
|
||||
AddShape {
|
||||
path: Vec<LayerId>,
|
||||
insert_index: isize,
|
||||
x0: f64,
|
||||
y0: f64,
|
||||
x1: f64,
|
||||
y1: f64,
|
||||
sides: u8,
|
||||
},
|
||||
DeleteLayer {
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
|
||||
126
core/document/src/shape_points.rs
Normal file
126
core/document/src/shape_points.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use std::{fmt, ops::Add};
|
||||
|
||||
use kurbo::{PathEl, Point, Vec2};
|
||||
use log::info;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ShapePoints {
|
||||
center: kurbo::Point,
|
||||
extent: kurbo::Vec2,
|
||||
sides: u8,
|
||||
}
|
||||
|
||||
impl ShapePoints {
|
||||
/// A new shape from center, a point and the number of points.
|
||||
#[inline]
|
||||
pub fn new(center: impl Into<Point>, extent: impl Into<Vec2>, sides: u8) -> ShapePoints {
|
||||
ShapePoints {
|
||||
center: center.into(),
|
||||
extent: extent.into(),
|
||||
sides: sides,
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the angle in radians between the longest line from the center and the apothem.
|
||||
#[inline]
|
||||
pub fn apothem_offset_angle(&self) -> f64 {
|
||||
std::f64::consts::PI / (self.sides as f64)
|
||||
}
|
||||
|
||||
// Gets the apothem (the shortest distance from the center to the edge)
|
||||
#[inline]
|
||||
pub fn apothem(&self) -> f64 {
|
||||
self.apothem_offset_angle().cos() * (self.sides as f64)
|
||||
}
|
||||
|
||||
// Gets the length of one side
|
||||
#[inline]
|
||||
pub fn side_length(&self) -> f64 {
|
||||
self.apothem_offset_angle().sin() * (self.sides as f64) * (2 as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ShapePoints {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn rotate(v: &Vec2, theta: f64) -> Vec2 {
|
||||
let cosine = theta.cos();
|
||||
let sine = theta.sin();
|
||||
return Vec2::new(v.x * cosine - v.y * sine, v.x * sine + v.y * cosine);
|
||||
}
|
||||
info!("sides{}", self.sides);
|
||||
for i in 0..self.sides {
|
||||
let radians = self.apothem_offset_angle() * ((i * 2 + (self.sides % 2)) as f64);
|
||||
let offset = rotate(&self.extent, radians);
|
||||
let point = self.center + offset;
|
||||
write!(f, "{},{} ", point.x, point.y)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ShapePathIter {
|
||||
shape: ShapePoints,
|
||||
ix: usize,
|
||||
}
|
||||
|
||||
impl Iterator for ShapePathIter {
|
||||
type Item = PathEl;
|
||||
|
||||
fn next(&mut self) -> Option<PathEl> {
|
||||
fn rotate(v: &Vec2, theta: f64) -> Vec2 {
|
||||
let cosine = theta.cos();
|
||||
let sine = theta.sin();
|
||||
return Vec2::new(v.x * cosine - v.y * sine, v.x * sine + v.y * cosine);
|
||||
}
|
||||
self.ix += 1;
|
||||
match self.ix {
|
||||
1 => Some(PathEl::MoveTo(self.shape.center + self.shape.extent)),
|
||||
_ => {
|
||||
let radians = self.shape.apothem_offset_angle() * ((self.ix * 2 + (self.shape.sides % 2) as usize) as f64);
|
||||
let offset = rotate(&self.shape.extent, radians);
|
||||
let point = self.shape.center + offset;
|
||||
Some(PathEl::LineTo(point))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Vec2> for ShapePoints {
|
||||
type Output = ShapePoints;
|
||||
|
||||
#[inline]
|
||||
fn add(self, movement: Vec2) -> ShapePoints {
|
||||
ShapePoints {
|
||||
center: self.center + movement,
|
||||
extent: self.extent,
|
||||
sides: self.sides,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl kurbo::Shape for ShapePoints {
|
||||
type PathElementsIter = ShapePathIter;
|
||||
#[inline]
|
||||
fn perimeter(&self, _accuracy: f64) -> f64 {
|
||||
self.side_length() * (self.sides as f64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn area(&self) -> f64 {
|
||||
self.apothem() * self.perimeter(2.1)
|
||||
}
|
||||
|
||||
fn path_elements(&self, _tolerance: f64) -> Self::PathElementsIter {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn winding(&self, _pt: Point) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bounding_box(&self) -> kurbo::Rect {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user