M src/document.rs => src/document.rs +61 -8
@@ 1,6 1,8 @@
use color;
use types::*;
+use std::borrow::Cow;
+
use rand::Rng;
pub struct Document {
@@ 50,7 52,8 @@ impl<T> AsMut<T> for Object<T> {
}
pub enum Element {
- Rect(Rect)
+ Rect(Rect),
+ Ellipse(Ellipse),
}
impl Element {
@@ 59,44 62,59 @@ impl Element {
Element::Rect(rect) => AABB {
position: rect.position.clone(),
size: rect.size.clone(),
- }
+ },
+ Element::Ellipse(ellipse) => AABB {
+ position: ellipse.center.clone() - &ellipse.radius,
+ size: ellipse.radius.clone() * 2.0,
+ },
}
}
pub fn as_scalable_mut(&mut self) -> Option<&mut ScalableElement> {
match self {
Element::Rect(rect) => Some(rect),
+ Element::Ellipse(ellipse) => Some(ellipse),
}
}
pub fn as_scalable(&self) -> Option<&ScalableElement> {
match self {
Element::Rect(rect) => Some(rect),
+ Element::Ellipse(ellipse) => Some(ellipse),
}
}
pub fn translate(&mut self, diff: &Size2) {
match self {
Element::Rect(rect) => {
rect.position += &diff;
- }
+ },
+ Element::Ellipse(ellipse) => {
+ ellipse.center += &diff;
+ },
}
}
pub fn presentation(&self) -> Option<&Presentation> {
match self {
Element::Rect(rect) => {
Some(&rect.presentation)
- }
+ },
+ Element::Ellipse(ellipse) => {
+ Some(&ellipse.presentation)
+ },
}
}
pub fn presentation_mut(&mut self) -> Option<&mut Presentation> {
match self {
Element::Rect(rect) => {
Some(&mut rect.presentation)
- }
+ },
+ Element::Ellipse(ellipse) => {
+ Some(&mut ellipse.presentation)
+ },
}
}
}
pub trait ScalableElement {
- fn get_size(&self) -> &Size2;
+ fn get_size(&self) -> Cow<Size2>;
fn set_size(&mut self, size: Size2);
}
@@ 108,8 126,8 @@ pub struct Rect {
}
impl ScalableElement for Rect {
- fn get_size(&self) -> &Size2 {
- &self.size
+ fn get_size(&self) -> Cow<Size2> {
+ Cow::Borrowed(&self.size)
}
fn set_size(&mut self, size: Size2) {
self.size = size;
@@ 126,6 144,41 @@ impl ScalableElement for Rect {
}
}
+pub struct Ellipse {
+ pub center: Point2,
+ pub radius: Size2,
+ pub presentation: Presentation,
+}
+
+impl ScalableElement for Ellipse {
+ fn get_size(&self) -> Cow<Size2> {
+ Cow::Owned(self.radius.clone() * 2.0)
+ }
+ fn set_size(&mut self, size: Size2) {
+ let radius = size / 2.0;
+
+ let mut diff = radius.clone() - &self.radius;
+ if radius.width < 0.0 {
+ diff.width -= radius.width;
+ }
+ if radius.height < 0.0 {
+ diff.height -= radius.height;
+ }
+ self.center += &diff;
+
+ self.radius = radius;
+
+ if self.radius.width < 0.0 {
+ self.center.x += self.radius.width;
+ self.radius.width = -self.radius.width;
+ }
+ if self.radius.height < 0.0 {
+ self.center.y += self.radius.height;
+ self.radius.height = -self.radius.height;
+ }
+ }
+}
+
#[derive(Clone)]
pub struct Presentation {
pub fill: Option<Paint>,
M src/draw.rs => src/draw.rs +38 -1
@@ 30,6 30,43 @@ pub fn draw_elem(elem: &Element, gr: &cairo::Context, ctx: DrawContext, drag_sta
}
gr.fill();
}
- }
+ },
+ Element::Ellipse(ellipse) => {
+ let mut pos = Cow::Borrowed(&ellipse.center);
+ if let Some(DragState::TranslateBoth { start_pos }) = drag_state {
+ let diff = ctx.mouse_pos - start_pos;
+ pos = Cow::Owned(pos.as_ref() + &diff);
+ }
+
+ let mut size = Cow::Borrowed(&ellipse.radius);
+ if let Some(DragState::ScaleBoth { start_pos }) = drag_state {
+ let diff = ctx.mouse_pos - start_pos;
+ let diff = diff / 2.0;
+ pos = pos + &diff;
+ size = Cow::Owned(size.as_ref() + &diff);
+ }
+
+ if size.width == 0.0 || size.height == 0.0 {
+ return;
+ }
+
+ gr.save();
+
+ gr.translate(pos.x, pos.y);
+ gr.scale(size.width, size.height);
+ gr.arc(0.0, 0.0, 1.0, 0.0, std::f64::consts::PI * 2.0);
+
+ gr.restore();
+
+ if let Some(ref fill) = ellipse.presentation.fill {
+ match fill {
+ Paint::Color(ref color) => match color {
+ color::Color::RGB(rgb) => gr.set_source_rgb(rgb.r as f64 / 255.0, rgb.g as f64 / 255.0, rgb.b as f64 / 255.0),
+ color::Color::RGBA(rgba) => gr.set_source_rgba(rgba.r as f64 / 255.0, rgba.g as f64 / 255.0, rgba.b as f64 / 255.0, rgba.a as f64),
+ }
+ }
+ gr.fill();
+ }
+ },
}
}
M src/main.rs => src/main.rs +39 -4
@@ 58,9 58,11 @@ fn main() {
});
}
- for (tool, icon) in &[
- (ToolMode::Rectangle, >k::STOCK_ADD)
- ] {
+ let list: &[(ToolMode, &'static str)] = &[
+ (ToolMode::Rectangle, >k::STOCK_ADD),
+ (ToolMode::Ellipse, >k::STOCK_CDROM),
+ ];
+ for (tool, icon) in list {
let instance = instance.clone();
let tool = *tool;
@@ 270,6 272,38 @@ fn main() {
instance.queue_repaint();
},
+ ToolMode::Ellipse => {
+ let mut document = instance.document.write().unwrap();
+
+ let position = Point2 {
+ x: pos_x,
+ y: pos_y,
+ };
+
+ let obj = Object::new_random_id(Element::Ellipse(Ellipse {
+ center: position.clone(),
+ presentation: Presentation {
+ fill: Some(Paint::Color(color::Color::RGB(color::RGB {
+ r: 0,
+ g: 0,
+ b: 255,
+ }))),
+ },
+ radius: Size2 {
+ width: 0.0,
+ height: 0.,
+ },
+ }));
+
+ *instance.selection.write().unwrap() = Some(obj.id());
+ instance.queue_update_sidebar();
+ *instance.drag_state.write().unwrap() = Some(DragState::ScaleBoth {
+ start_pos: position,
+ });
+ document.content.push(obj);
+
+ instance.queue_repaint();
+ },
ToolMode::Select => {
let document = instance.document.read().unwrap();
@@ 356,7 390,7 @@ fn main() {
if elem.id() == selection {
if let Some(scalable) = elem.as_mut().as_scalable_mut() {
println!("scaling");
- let new_size = scalable.get_size() + &diff;
+ let new_size = scalable.get_size().into_owned() + &diff;
scalable.set_size(new_size);
instance.queue_repaint();
}
@@ 568,6 602,7 @@ impl EditInstance {
enum ToolMode {
Select,
Rectangle,
+ Ellipse,
}
pub enum DragState {
M src/save.rs => src/save.rs +24 -1
@@ 68,7 68,30 @@ fn write_element_svg(elem: &document::Element, writer: &mut quick_xml::Writer<im
.with_attributes(attrs.into_iter())))?;
writer.write_event(quick_xml::events::Event::End(quick_xml::events::BytesEnd::borrowed(b"rect")))?;
Ok(())
- }
+ },
+ document::Element::Ellipse(ellipse) => {
+ let x_str = ellipse.center.x.to_string();
+ let y_str = ellipse.center.y.to_string();
+ let width_str = ellipse.radius.width.to_string();
+ let height_str = ellipse.radius.height.to_string();
+ let mut attrs = vec![
+ ("cx", &x_str[..]).into(),
+ ("cy", &y_str[..]).into(),
+ ("rx", &width_str[..]).into(),
+ ("ry", &height_str[..]).into(),
+ ];
+ if let Some(ref fill) = ellipse.presentation.fill {
+ let fill_str = paint_to_str(&fill);
+ attrs.push(quick_xml::events::attributes::Attribute {
+ key: b"fill",
+ value: Cow::Owned(fill_str.as_bytes().to_owned()),
+ });
+ }
+ writer.write_event(quick_xml::events::Event::Start(quick_xml::events::BytesStart::borrowed_name(b"ellipse")
+ .with_attributes(attrs.into_iter())))?;
+ writer.write_event(quick_xml::events::Event::End(quick_xml::events::BytesEnd::borrowed(b"ellipse")))?;
+ Ok(())
+ },
}
}
M src/types.rs => src/types.rs +87 -0
@@ 1,3 1,5 @@
+use std::borrow::Cow;
+
#[derive(Clone)]
pub struct Point2 {
pub x: f64,
@@ 15,6 17,16 @@ impl<'a> std::ops::Add<&'a Size2> for &'a Point2 {
}
}
+impl<'a> std::ops::Add<&'a Size2> for Point2 {
+ type Output = Point2;
+
+ fn add(mut self, other: &Size2) -> Point2 {
+ self.x += other.width;
+ self.y += other.height;
+ self
+ }
+}
+
impl<'a> std::ops::AddAssign<&'a Size2> for Point2 {
fn add_assign(&mut self, other: &Size2) {
self.x += other.width;
@@ 22,6 34,17 @@ impl<'a> std::ops::AddAssign<&'a Size2> for Point2 {
}
}
+impl<'a> std::ops::Add<&'a Size2> for Cow<'a, Point2> {
+ type Output = Cow<'static, Point2>;
+
+ fn add(self, other: &Size2) -> Self::Output {
+ match self {
+ Cow::Borrowed(pt) => Cow::Owned(pt + other),
+ Cow::Owned(pt) => Cow::Owned(pt + other),
+ }
+ }
+}
+
impl<'a> std::ops::Sub for &'a Point2 {
type Output = Size2;
@@ 44,6 67,17 @@ impl std::ops::Sub<Size2> for Point2 {
}
}
+impl<'a> std::ops::Sub<&'a Size2> for Point2 {
+ type Output = Point2;
+
+ fn sub(mut self, other: &Size2) -> Point2 {
+ self.x -= other.width;
+ self.y -= other.height;
+
+ self
+ }
+}
+
#[derive(Clone)]
pub struct Size2 {
pub width: f64,
@@ 61,6 95,53 @@ impl<'a> std::ops::Add for &'a Size2 {
}
}
+impl<'a> std::ops::Add<&'a Size2> for Size2 {
+ type Output = Size2;
+
+ fn add(mut self, other: &Size2) -> Size2 {
+ self.width += other.width;
+ self.height += other.height;
+ self
+ }
+}
+
+impl<'a> std::ops::Sub<&'a Size2> for Size2 {
+ type Output = Size2;
+
+ fn sub(mut self, other: &Size2) -> Size2 {
+ self.width -= other.width;
+ self.height -= other.height;
+ self
+ }
+}
+
+impl std::ops::Mul<f64> for Size2 {
+ type Output = Size2;
+
+ fn mul(mut self, other: f64) -> Size2 {
+ self.width *= other;
+ self.height *= other;
+ self
+ }
+}
+
+impl std::ops::Div<f64> for Size2 {
+ type Output = Size2;
+
+ fn div(mut self, other: f64) -> Size2 {
+ self.width /= other;
+ self.height /= other;
+ self
+ }
+}
+
+impl std::ops::DivAssign<f64> for Size2 {
+ fn div_assign(&mut self, other: f64) {
+ self.width /= other;
+ self.height /= other;
+ }
+}
+
impl Size2 {
pub fn new(width: f64, height: f64) -> Self {
Size2 {
@@ 68,6 149,12 @@ impl Size2 {
height,
}
}
+
+ pub fn abs(mut self) -> Self {
+ self.width = self.width.abs();
+ self.height = self.height.abs();
+ self
+ }
}
pub struct AABB {