~cbondurant/lipuma-devel

Conner Bondurant: 2
 Implementation of selection box
 First attempt at selection logic

 22 files changed, 326 insertions(+), 97 deletions(-)
I am going to consider this patch finalized thanks to an additional 
commit I have written removing the noise dependency and improving noise 
speed as a result.

- Conner Bondurant
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~cbondurant/lipuma-devel/patches/36556/mbox | git am -3
Learn more about email & git

[PATCH 1/2] Implementation of selection box Export this patch

---
 Cargo.lock                           | 29 -------------
 Cargo.toml                           |  2 -
 src/draw_tools/fractal_line_tool.rs  | 14 +++---
 src/draw_tools/mod.rs                |  1 +
 src/draw_tools/selection_tool.rs     | 65 ++++++++++++++++++++++++++++
 src/draw_tools/tool.rs               | 11 ++---
 src/main.rs                          | 45 ++++++++++++-------
 src/render_objects/drawable.rs       |  8 ++--
 src/render_objects/fractal_line.rs   | 12 ++---
 src/render_objects/mod.rs            |  1 +
 src/render_objects/render_object.rs  |  2 +-
 src/render_objects/selection_rect.rs | 43 ++++++++++++++++++
 src/widgets/graphics_data.rs         |  5 ++-
 src/widgets/graphics_scene_widget.rs | 10 +++--
 src/widgets/tool_selector.rs         | 50 +++++++++++++++++++++
 src/widgets/tool_selector_widget.rs  |  0
 16 files changed, 221 insertions(+), 77 deletions(-)
 create mode 100644 src/draw_tools/selection_tool.rs
 create mode 100644 src/render_objects/selection_rect.rs
 create mode 100644 src/widgets/tool_selector.rs
 create mode 100644 src/widgets/tool_selector_widget.rs

diff --git a/Cargo.lock b/Cargo.lock
index 3f7145c..0815524 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -434,33 +434,6 @@ dependencies = [
 "wio",
]

[[package]]
name = "dyn-clonable"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4"
dependencies = [
 "dyn-clonable-impl",
 "dyn-clone",
]

[[package]]
name = "dyn-clonable-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "dyn-clone"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"

[[package]]
name = "either"
version = "1.8.0"
@@ -1466,8 +1439,6 @@ name = "rust-lipuma"
version = "0.1.0"
dependencies = [
 "druid",
 "dyn-clonable",
 "dyn-clone",
 "noise",
 "rand 0.8.5",
]
diff --git a/Cargo.toml b/Cargo.toml
index 6b1fb47..7fc6609 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,5 @@ edition = "2021"

[dependencies]
druid = {version = "0.7.0", features = ["im", "svg", "image"]}
dyn-clonable = "0.9.0"
dyn-clone = "1.0.9"
noise = "0.8.1"
rand = "0.8.5"
diff --git a/src/draw_tools/fractal_line_tool.rs b/src/draw_tools/fractal_line_tool.rs
index c766594..f78679f 100644
--- a/src/draw_tools/fractal_line_tool.rs
+++ b/src/draw_tools/fractal_line_tool.rs
@@ -3,17 +3,17 @@ use noise::OpenSimplex;
use rand::random;
use std::rc::Rc;

use super::tool::Tool;
use crate::render_objects::{fractal_line::FractalLine, RenderObject};
use crate::widgets::graphics_data::GraphicsData;

#[derive(Data, Clone, PartialEq, Eq)]
use super::tool::Tool;

#[derive(Data, Clone, PartialEq, Eq, Debug)]
enum ToolState {
	Drawing,
	Standby,
}

#[derive(Data, Clone)]
#[derive(Data, Debug, Clone)]
pub struct FractalLineTool {
	preview: FractalLine,
	state: ToolState,
@@ -94,15 +94,15 @@ impl FractalLineTool {
}

impl Tool for FractalLineTool {
	fn enable(&mut self, _data: &mut GraphicsData) {
	fn enable(&mut self, _data: &mut OrdSet<RenderObject>) {
		self.state = ToolState::Standby;
	}

	fn disable(&mut self, data: &mut GraphicsData) {
	fn disable(&mut self, data: &mut OrdSet<RenderObject>) {
		match self.state {
			ToolState::Drawing => {
				// get_preview always returns some when drawing
				data.objects.insert(self.get_preview().unwrap());
				data.insert(self.get_preview().unwrap());
			}
			ToolState::Standby => (),
		}
diff --git a/src/draw_tools/mod.rs b/src/draw_tools/mod.rs
index 033ef91..03c110c 100644
--- a/src/draw_tools/mod.rs
+++ b/src/draw_tools/mod.rs
@@ -1,2 +1,3 @@
pub mod fractal_line_tool;
pub mod selection_tool;
pub mod tool;
diff --git a/src/draw_tools/selection_tool.rs b/src/draw_tools/selection_tool.rs
new file mode 100644
index 0000000..f12e6ec
--- /dev/null
+++ b/src/draw_tools/selection_tool.rs
@@ -0,0 +1,65 @@
use std::rc::Rc;

use super::tool::Tool;
use druid::{im::OrdSet, Data, Event, Point, Rect};

use crate::render_objects::{selection_rect::SelectionRect, RenderObject};

#[derive(Data, Debug, Clone)]
pub struct SelectionTool {
	start_coord: Point,
	end_coord: Point,
	is_active: bool,
}

impl SelectionTool {
	pub fn new() -> Self {
		Self {
			start_coord: Point::ZERO,
			end_coord: Point::ZERO,
			is_active: false,
		}
	}
}

impl Tool for SelectionTool {
	fn enable(&mut self, _data: &mut OrdSet<RenderObject>) {
		()
	}

	fn disable(&mut self, _data: &mut OrdSet<RenderObject>) {
		()
	}

	fn event(
		&mut self,
		event: &druid::Event,
		_ctx: &mut druid::EventCtx,
		data: OrdSet<RenderObject>,
	) -> OrdSet<RenderObject> {
		match event {
			Event::MouseDown(e) => {
				self.is_active = true;
				self.start_coord = e.pos;
			}
			Event::MouseUp(_) => self.is_active = false,
			Event::MouseMove(e) => self.end_coord = e.pos,
			_ => (),
		}
		data
	}

	fn get_preview(&self) -> Option<RenderObject> {
		if self.is_active {
			Some(RenderObject::new(
				u32::MAX,
				Rc::new(Box::new(SelectionRect::new(Rect::from_points(
					self.start_coord,
					self.end_coord,
				)))),
			))
		} else {
			None
		}
	}
}
diff --git a/src/draw_tools/tool.rs b/src/draw_tools/tool.rs
index bf11961..f052e8d 100644
--- a/src/draw_tools/tool.rs
+++ b/src/draw_tools/tool.rs
@@ -1,12 +1,9 @@
use crate::{render_objects::RenderObject, GraphicsData};
use crate::render_objects::RenderObject;
use druid::{im::OrdSet, Event, EventCtx};
use dyn_clonable::*;

#[clonable]
pub trait Tool: Clone {
	fn enable(&mut self, data: &mut GraphicsData);
	fn disable(&mut self, data: &mut GraphicsData);

pub trait Tool {
	fn enable(&mut self, data: &mut OrdSet<RenderObject>);
	fn disable(&mut self, data: &mut OrdSet<RenderObject>);
	fn event(
		&mut self,
		event: &Event,
diff --git a/src/main.rs b/src/main.rs
index 94350fa..ce1093c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,11 @@
use std::sync::{Arc, Mutex};

use draw_tools::fractal_line_tool::FractalLineTool;
use draw_tools::selection_tool::SelectionTool;
use draw_tools::tool::Tool;
use druid::im::ordset;
use druid::widget::Flex;
use druid::{theme, AppLauncher, Color, PlatformError, Widget, WidgetExt, WindowDesc};
use std::rc::Rc;
use druid::widget::{Button, Flex};
use druid::{AppLauncher, PlatformError, Widget, WindowDesc};

mod draw_tools;
mod render_objects;
@@ -11,22 +14,32 @@ use widgets::{graphics_data::GraphicsData, graphics_scene_widget::*};

fn build_ui() -> impl Widget<GraphicsData> {
	let mut row = Flex::row();
	row.add_child(
		Flex::column()
			.with_child(Button::new("Fractal Line Tool").on_click(
				|_ctx, data: &mut GraphicsData, _env| {
					data.tool.lock().unwrap().disable(&mut data.objects);
					data.tool = Arc::new(Mutex::new(FractalLineTool::new()));
					data.tool.lock().unwrap().enable(&mut data.objects);
				},
			))
			.with_child(Button::new("Selection Tool").on_click(
				|_ctx, data: &mut GraphicsData, _env| {
					data.tool.lock().unwrap().disable(&mut data.objects);
					data.tool = Arc::new(Mutex::new(SelectionTool::new()));
					data.tool.lock().unwrap().enable(&mut data.objects);
				},
			)),
	);
	row.add_flex_child(GraphicsWidget::new(), 1.0);
	row.debug_paint_layout()
	row
}

fn main() -> Result<(), PlatformError> {
	AppLauncher::with_window(WindowDesc::new(build_ui))
		.configure_env(|env, _| {
			env.set(
				theme::WINDOW_BACKGROUND_COLOR,
				Color::rgba(0.0, 0.0, 0.0, 0.0),
			)
		})
		.launch(GraphicsData {
			objects: ordset![],
			preview: None,
			tool: Rc::new(Box::new(FractalLineTool::new())),
		})?;
	AppLauncher::with_window(WindowDesc::new(build_ui)).launch(GraphicsData {
		objects: ordset![],
		preview: None,
		tool: Arc::new(Mutex::new(FractalLineTool::new())),
	})?;
	Ok(())
}
diff --git a/src/render_objects/drawable.rs b/src/render_objects/drawable.rs
index 9407c74..ec3a2da 100644
--- a/src/render_objects/drawable.rs
+++ b/src/render_objects/drawable.rs
@@ -1,4 +1,6 @@
use druid::{Affine, Rect};
use druid::Rect;

use super::RenderObject;

pub trait Drawable {
	#[allow(non_snake_case)]
@@ -9,7 +11,7 @@ pub trait Drawable {
		ctx: &mut druid::EventCtx,
		event: &druid::Event,
		env: &druid::Env,
		sctx: &mut Affine,
		sctx: &RenderObject,
	);
	fn paint(&self, ctx: &mut druid::PaintCtx, env: &druid::Env, sctx: &Affine);
	fn paint(&self, ctx: &mut druid::PaintCtx, env: &druid::Env, sctx: &RenderObject);
}
diff --git a/src/render_objects/fractal_line.rs b/src/render_objects/fractal_line.rs
index 89426fa..cef41bd 100644
--- a/src/render_objects/fractal_line.rs
+++ b/src/render_objects/fractal_line.rs
@@ -1,13 +1,13 @@
use druid::{
	kurbo::{PathEl, Shape},
	Affine, Color, Data, Point, Rect, RenderContext, Vec2,
	Color, Data, Point, Rect, RenderContext, Vec2,
};
use noise::{NoiseFn, OpenSimplex};
use std::rc::Rc;

use super::drawable::Drawable;
use super::{drawable::Drawable, RenderObject};

#[derive(Data, Clone)]
#[derive(Data, Clone, Debug)]
pub struct FractalLine {
	pub start: Point,
	pub end: Point,
@@ -85,7 +85,7 @@ impl Shape for FractalLine {
	}

	fn bounding_box(&self) -> Rect {
		Rect::from_points(self.start, self.end).inflate(self.width, self.width * 1.5)
		Rect::from_points(self.start, self.end).inflate(self.width * 1.5, self.width * 1.5)
	}
}

@@ -100,12 +100,12 @@ impl Drawable for FractalLine {
		ctx: &mut druid::EventCtx,
		event: &druid::Event,
		env: &druid::Env,
		sctx: &mut Affine,
		sctx: &RenderObject,
	) {
		todo!()
	}

	fn paint(&self, ctx: &mut druid::PaintCtx, _env: &druid::Env, _sctx: &Affine) {
	fn paint(&self, ctx: &mut druid::PaintCtx, _env: &druid::Env, _sctx: &RenderObject) {
		ctx.stroke(self, &Color::BLACK, 1.0);
	}
}
diff --git a/src/render_objects/mod.rs b/src/render_objects/mod.rs
index 04b766d..5babdd4 100644
--- a/src/render_objects/mod.rs
+++ b/src/render_objects/mod.rs
@@ -2,3 +2,4 @@ mod drawable;
pub mod fractal_line;
pub mod render_object;
pub use render_object::RenderObject;
pub mod selection_rect;
diff --git a/src/render_objects/render_object.rs b/src/render_objects/render_object.rs
index 0780fd1..9c4d76d 100644
--- a/src/render_objects/render_object.rs
+++ b/src/render_objects/render_object.rs
@@ -59,7 +59,7 @@ impl RenderObject {
			newctx.transform(self.transform);
			//newctx.clip(self.drawable.AABB());
			//newctx.fill(self.drawable.AABB(), &Color::WHITE);
			self.drawable.paint(newctx, env, &self.transform);
			self.drawable.paint(newctx, env, &self);
		});
	}

diff --git a/src/render_objects/selection_rect.rs b/src/render_objects/selection_rect.rs
new file mode 100644
index 0000000..adebba1
--- /dev/null
+++ b/src/render_objects/selection_rect.rs
@@ -0,0 +1,43 @@
use druid::{
	piet::{PaintBrush, StrokeStyle},
	Color, Rect, RenderContext,
};

use super::drawable::Drawable;

const SELECTION_BRUSH: PaintBrush = PaintBrush::Color(Color::BLACK);

pub struct SelectionRect {
	rect: Rect,
}

impl SelectionRect {
	pub fn new(rect: Rect) -> Self {
		Self { rect }
	}
}

impl Drawable for SelectionRect {
	fn AABB(&self) -> Rect {
		self.rect.inflate(1.0, 1.0)
	}

	fn event(
		&mut self,
		_ctx: &mut druid::EventCtx,
		_event: &druid::Event,
		_env: &druid::Env,
		_sctx: &super::RenderObject,
	) {
		todo!()
	}

	fn paint(&self, ctx: &mut druid::PaintCtx, env: &druid::Env, sctx: &super::RenderObject) {
		ctx.stroke_styled(
			self.rect,
			&SELECTION_BRUSH,
			1.0,
			&StrokeStyle::new().dash(vec![3.0, 3.0], 0.0),
		);
	}
}
diff --git a/src/widgets/graphics_data.rs b/src/widgets/graphics_data.rs
index 1aebd51..a4ef689 100644
--- a/src/widgets/graphics_data.rs
+++ b/src/widgets/graphics_data.rs
@@ -1,12 +1,13 @@
use std::sync::{Arc, Mutex};

use crate::draw_tools::tool::Tool;
use crate::render_objects::RenderObject;
use druid::im::OrdSet;
use druid::Data;
use std::rc::Rc;

#[derive(Data, Clone)]
pub struct GraphicsData {
	pub objects: OrdSet<RenderObject>,
	pub preview: Option<RenderObject>,
	pub tool: Rc<Box<dyn Tool>>,
	pub tool: Arc<Mutex<dyn Tool>>,
}
diff --git a/src/widgets/graphics_scene_widget.rs b/src/widgets/graphics_scene_widget.rs
index 76cba53..1690364 100644
--- a/src/widgets/graphics_scene_widget.rs
+++ b/src/widgets/graphics_scene_widget.rs
@@ -34,8 +34,11 @@ impl Widget<GraphicsData> for GraphicsWidget {
		data: &mut GraphicsData,
		_env: &druid::Env,
	) {
		let muttool = Rc::make_mut(&mut data.tool);
		data.objects = muttool.event(event, ctx, data.objects.clone());
		data.objects = data
			.tool
			.lock()
			.unwrap()
			.event(event, ctx, data.objects.clone());
		if !ctx.is_handled() {
			#[allow(clippy::single_match)]
			// We expect to match other expressions later, but this is the only one that matters now
@@ -47,8 +50,7 @@ impl Widget<GraphicsData> for GraphicsWidget {
				_ => (),
			}
		}
		data.preview = muttool.get_preview();
		data.tool = Rc::new(muttool.clone());
		data.preview = data.tool.lock().unwrap().get_preview();
	}

	fn lifecycle(
diff --git a/src/widgets/tool_selector.rs b/src/widgets/tool_selector.rs
new file mode 100644
index 0000000..4e72407
--- /dev/null
+++ b/src/widgets/tool_selector.rs
@@ -0,0 +1,50 @@
use super::graphics_data::GraphicsData;
use druid::Widget;

struct ToolSelector {}

impl Widget<GraphicsData> for ToolSelector {
	fn event(
		&mut self,
		ctx: &mut druid::EventCtx,
		event: &druid::Event,
		data: &mut GraphicsData,
		env: &druid::Env,
	) {
		todo!()
	}

	fn lifecycle(
		&mut self,
		ctx: &mut druid::LifeCycleCtx,
		event: &druid::LifeCycle,
		data: &GraphicsData,
		env: &druid::Env,
	) {
		todo!()
	}

	fn update(
		&mut self,
		ctx: &mut druid::UpdateCtx,
		old_data: &GraphicsData,
		data: &GraphicsData,
		env: &druid::Env,
	) {
		todo!()
	}

	fn layout(
		&mut self,
		ctx: &mut druid::LayoutCtx,
		bc: &druid::BoxConstraints,
		data: &GraphicsData,
		env: &druid::Env,
	) -> druid::Size {
		todo!()
	}

	fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &GraphicsData, env: &druid::Env) {
		todo!()
	}
}
diff --git a/src/widgets/tool_selector_widget.rs b/src/widgets/tool_selector_widget.rs
new file mode 100644
index 0000000..e69de29
-- 
2.34.1

[PATCH 2/2] First attempt at selection logic Export this patch

---
 src/draw_tools/fractal_line_tool.rs  |  8 +++-
 src/draw_tools/selection_tool.rs     | 62 +++++++++++++++++++++++-----
 src/render_objects/drawable.rs       |  3 +-
 src/render_objects/fractal_line.rs   | 26 ++++++++++--
 src/render_objects/render_object.rs  | 21 ++++++++--
 src/render_objects/selection_rect.rs |  5 +++
 6 files changed, 105 insertions(+), 20 deletions(-)

diff --git a/src/draw_tools/fractal_line_tool.rs b/src/draw_tools/fractal_line_tool.rs
index f78679f..ea09f81 100644
--- a/src/draw_tools/fractal_line_tool.rs
+++ b/src/draw_tools/fractal_line_tool.rs
@@ -28,7 +28,9 @@ impl FractalLineTool {
				noise: Rc::new(OpenSimplex::new(0)),
				width: 10.0,
				density: 0.05,
				samples: 1000,
				samples: 500,
				laurancity: 0.5,
				octaves: 4,
			},
			state: ToolState::Standby,
		}
@@ -62,7 +64,9 @@ impl FractalLineTool {
			noise: Rc::new(OpenSimplex::new(random())),
			width: 10.0,
			density: 0.05,
			samples: 1000,
			samples: 500,
			laurancity: 0.5,
			octaves: 4,
		};
		ctx.set_handled();
		data
diff --git a/src/draw_tools/selection_tool.rs b/src/draw_tools/selection_tool.rs
index f12e6ec..b90be5f 100644
--- a/src/draw_tools/selection_tool.rs
+++ b/src/draw_tools/selection_tool.rs
@@ -5,11 +5,17 @@ use druid::{im::OrdSet, Data, Event, Point, Rect};

use crate::render_objects::{selection_rect::SelectionRect, RenderObject};

#[derive(Data, Debug, Clone, PartialEq, Eq)]
enum SelectionState {
	Active,
	Inactive,
}

#[derive(Data, Debug, Clone)]
pub struct SelectionTool {
	start_coord: Point,
	end_coord: Point,
	is_active: bool,
	state: SelectionState,
}

impl SelectionTool {
@@ -17,9 +23,40 @@ impl SelectionTool {
		Self {
			start_coord: Point::ZERO,
			end_coord: Point::ZERO,
			is_active: false,
			state: SelectionState::Inactive,
		}
	}

	fn update_selected(&self, mut data: OrdSet<RenderObject>) -> OrdSet<RenderObject> {
		let bound = Rect::from_points(self.start_coord, self.end_coord);
		'outer: for item in &data.clone() {
			if !bound.intersect(item.drawable.AABB()).is_empty() {
				for segment in item.drawable.fine_collision_shape(1.0) {
					match segment {
						druid::kurbo::PathEl::MoveTo(p)
						| druid::kurbo::PathEl::LineTo(p)
						| druid::kurbo::PathEl::QuadTo(_, p)
						| druid::kurbo::PathEl::CurveTo(_, _, p) => {
							if bound.contains(p) {
								let mut new_item = item.clone();
								new_item.select();
								data.remove(item);
								data.insert(new_item);
								continue 'outer;
							}
						}
						druid::kurbo::PathEl::ClosePath => todo!(),
					}
				}
			}

			let mut new_item = item.clone();
			new_item.deselect();
			data.remove(item);
			data.insert(new_item);
		}
		data
	}
}

impl Tool for SelectionTool {
@@ -39,27 +76,32 @@ impl Tool for SelectionTool {
	) -> OrdSet<RenderObject> {
		match event {
			Event::MouseDown(e) => {
				self.is_active = true;
				self.state = SelectionState::Active;
				self.start_coord = e.pos;
				self.end_coord = e.pos;
			}
			Event::MouseUp(_) => self.state = SelectionState::Inactive,
			Event::MouseMove(e) => {
				if let SelectionState::Active = self.state {
					self.end_coord = e.pos;
					return self.update_selected(data);
				}
			}
			Event::MouseUp(_) => self.is_active = false,
			Event::MouseMove(e) => self.end_coord = e.pos,
			_ => (),
		}
		data
	}

	fn get_preview(&self) -> Option<RenderObject> {
		if self.is_active {
			Some(RenderObject::new(
		match self.state {
			SelectionState::Active => Some(RenderObject::new(
				u32::MAX,
				Rc::new(Box::new(SelectionRect::new(Rect::from_points(
					self.start_coord,
					self.end_coord,
				)))),
			))
		} else {
			None
			)),
			SelectionState::Inactive => None,
		}
	}
}
diff --git a/src/render_objects/drawable.rs b/src/render_objects/drawable.rs
index ec3a2da..cd702a4 100644
--- a/src/render_objects/drawable.rs
+++ b/src/render_objects/drawable.rs
@@ -1,10 +1,11 @@
use druid::Rect;
use druid::{kurbo::BezPath, Rect};

use super::RenderObject;

pub trait Drawable {
	#[allow(non_snake_case)]
	fn AABB(&self) -> Rect;
	fn fine_collision_shape(&self, tolerance: f64) -> BezPath;

	fn event(
		&mut self,
diff --git a/src/render_objects/fractal_line.rs b/src/render_objects/fractal_line.rs
index cef41bd..9f7e39c 100644
--- a/src/render_objects/fractal_line.rs
+++ b/src/render_objects/fractal_line.rs
@@ -1,5 +1,5 @@
use druid::{
	kurbo::{PathEl, Shape},
	kurbo::{BezPath, PathEl, Shape},
	Color, Data, Point, Rect, RenderContext, Vec2,
};
use noise::{NoiseFn, OpenSimplex};
@@ -15,6 +15,8 @@ pub struct FractalLine {
	pub width: f64,
	pub density: f64,
	pub samples: i32,
	pub laurancity: f64,
	pub octaves: i8,
}

impl FractalLinePathIter {
@@ -57,7 +59,11 @@ impl Iterator for FractalLinePathIter {
		self.i += 1;

		let simplex_distance = self.real_length * index * self.line_data.density;
		let simplex = self.line_data.noise.get([simplex_distance, 0.0]) * 3.0;
		let mut simplex = 0.0;
		for i in 0..self.line_data.octaves {
			simplex += self.line_data.noise.get([simplex_distance * i as f64, 0.0])
				* 3.0 * self.line_data.laurancity.powi(i.into());
		}
		Some(druid::piet::kurbo::PathEl::LineTo(
			self.line_data.start.lerp(self.line_data.end, index)
				+ self.perpendicular * self.line_data.width * Self::smooth_to_zero(index) * simplex,
@@ -94,6 +100,10 @@ impl Drawable for FractalLine {
		self.bounding_box()
	}

	fn fine_collision_shape(&self, tolerance: f64) -> BezPath {
		self.to_path(tolerance)
	}

	#[allow(unused_variables)]
	fn event(
		&mut self,
@@ -105,7 +115,15 @@ impl Drawable for FractalLine {
		todo!()
	}

	fn paint(&self, ctx: &mut druid::PaintCtx, _env: &druid::Env, _sctx: &RenderObject) {
		ctx.stroke(self, &Color::BLACK, 1.0);
	fn paint(&self, ctx: &mut druid::PaintCtx, _env: &druid::Env, sctx: &RenderObject) {
		ctx.stroke(
			self,
			if sctx.is_selected() {
				&Color::RED
			} else {
				&Color::BLACK
			},
			1.0,
		);
	}
}
diff --git a/src/render_objects/render_object.rs b/src/render_objects/render_object.rs
index 9c4d76d..9d394d2 100644
--- a/src/render_objects/render_object.rs
+++ b/src/render_objects/render_object.rs
@@ -10,6 +10,7 @@ use std::rc::Rc;
pub struct RenderObject {
	pub z: u32,
	pub transform: Affine,
	pub selected: bool,
	pub drawable: Rc<Box<dyn Drawable>>,
}

@@ -47,7 +48,10 @@ impl PartialOrd for RenderObject {

impl PartialEq for RenderObject {
	fn eq(&self, other: &Self) -> bool {
		self.z == other.z && Rc::ptr_eq(&self.drawable, &other.drawable)
		self.z == other.z
			&& Rc::ptr_eq(&self.drawable, &other.drawable)
			&& self.selected == other.selected
			&& self.transform == other.transform
	}
}

@@ -57,8 +61,6 @@ impl RenderObject {
	pub fn paint(&self, ctx: &mut druid::PaintCtx, env: &druid::Env) {
		ctx.with_save(|newctx| {
			newctx.transform(self.transform);
			//newctx.clip(self.drawable.AABB());
			//newctx.fill(self.drawable.AABB(), &Color::WHITE);
			self.drawable.paint(newctx, env, &self);
		});
	}
@@ -68,6 +70,7 @@ impl RenderObject {
			z,
			transform: Affine::new([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]),
			drawable,
			selected: false,
		}
	}

@@ -80,4 +83,16 @@ impl RenderObject {
		ctx.transform(self.transform);
		ctx.with_save(|new_ctx| new_ctx.stroke(self.drawable.AABB(), &Color::RED, 1.0))
	}

	pub fn select(&mut self) {
		self.selected = true;
	}

	pub fn deselect(&mut self) {
		self.selected = false;
	}

	pub fn is_selected(&self) -> bool {
		self.selected
	}
}
diff --git a/src/render_objects/selection_rect.rs b/src/render_objects/selection_rect.rs
index adebba1..b0affef 100644
--- a/src/render_objects/selection_rect.rs
+++ b/src/render_objects/selection_rect.rs
@@ -1,4 +1,5 @@
use druid::{
	kurbo::Shape,
	piet::{PaintBrush, StrokeStyle},
	Color, Rect, RenderContext,
};
@@ -22,6 +23,10 @@ impl Drawable for SelectionRect {
		self.rect.inflate(1.0, 1.0)
	}

	fn fine_collision_shape(&self, tolerance: f64) -> druid::kurbo::BezPath {
		self.AABB().to_path(tolerance)
	}

	fn event(
		&mut self,
		_ctx: &mut druid::EventCtx,
-- 
2.34.1
I am going to consider this patch finalized thanks to an additional 
commit I have written removing the noise dependency and improving noise 
speed as a result.

- Conner Bondurant