~vpzom/savg

3ec25b0fd653c603200f97c04043e00f5ec36d5b — Colin Reeder 5 years ago ba7295d
Add ellipse support
5 files changed, 249 insertions(+), 14 deletions(-)

M src/document.rs
M src/draw.rs
M src/main.rs
M src/save.rs
M src/types.rs
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, &gtk::STOCK_ADD)
        ] {
        let list: &[(ToolMode, &'static str)] = &[
            (ToolMode::Rectangle, &gtk::STOCK_ADD),
            (ToolMode::Ellipse, &gtk::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 {