diff --git a/editor/src/messages/input_mapper/utility_types/input_mouse.rs b/editor/src/messages/input_mapper/utility_types/input_mouse.rs index 00635587..a9b74660 100644 --- a/editor/src/messages/input_mapper/utility_types/input_mouse.rs +++ b/editor/src/messages/input_mapper/utility_types/input_mouse.rs @@ -88,10 +88,9 @@ pub struct MouseState { impl MouseState { pub fn finish_transaction(&self, drag_start: DVec2, responses: &mut VecDeque) { - match drag_start.distance(self.position) <= DRAG_THRESHOLD { - true => responses.add(DocumentMessage::AbortTransaction), - false => responses.add(DocumentMessage::EndTransaction), - } + let drag_too_small = drag_start.distance(self.position) <= DRAG_THRESHOLD; + let response = if drag_too_small { DocumentMessage::AbortTransaction } else { DocumentMessage::EndTransaction }; + responses.add(response); } } diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index 9d1ca39e..90b312f9 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -174,10 +174,7 @@ impl MessageHandler> for Navigation responses.add(DocumentMessage::PTZUpdate); } NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { - let delta = match use_y_as_x { - false => -ipp.mouse.scroll_delta.as_dvec2(), - true => (-ipp.mouse.scroll_delta.y, 0.).into(), - } * VIEWPORT_SCROLL_RATE; + let delta = if use_y_as_x { (-ipp.mouse.scroll_delta.y, 0.).into() } else { -ipp.mouse.scroll_delta.as_dvec2() } * VIEWPORT_SCROLL_RATE; responses.add(NavigationMessage::CanvasPan { delta }); responses.add(NodeGraphMessage::SetGridAlignedEdges); } diff --git a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs index d9c9e2a3..87d4e07e 100644 --- a/editor/src/messages/portfolio/document/overlays/grid_overlays.rs +++ b/editor/src/messages/portfolio/document/overlays/grid_overlays.rs @@ -36,7 +36,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: } else { DVec2::new(secondary_pos, primary_end) }; - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color)); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); } } } @@ -105,7 +105,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m let x_pos = (((min_x - origin.x) / spacing).ceil() + line_index as f64) * spacing + origin.x; let start = DVec2::new(x_pos, min_y); let end = DVec2::new(x_pos, max_y); - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color)); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); } for (tan, multiply) in [(tan_a, -1.), (tan_b, 1.)] { @@ -119,7 +119,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m let y_pos = (((inverse_project(&min_y) - origin.y) / spacing).ceil() + line_index as f64) * spacing + origin.y; let start = DVec2::new(min_x, project(&DVec2::new(min_x, y_pos))); let end = DVec2::new(max_x, project(&DVec2::new(max_x, y_pos))); - overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color)); + overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None); } } } @@ -166,6 +166,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), + None, Some(1.), Some((spacing_x / cos_a) * document_to_viewport.matrix2.x_axis.length() - 1.), None, diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index b8697cb5..a6a77b17 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -59,17 +59,17 @@ fn overlay_bezier_handles(bezier: Bezier, segment_id: SegmentId, transform: DAff match bezier.handles { BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => { - overlay_context.line(handle, bezier.start, None); - overlay_context.line(handle, bezier.end, None); + overlay_context.line(handle, bezier.start, None, None); + overlay_context.line(handle, bezier.end, None, None); overlay_context.manipulator_handle(handle, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None); } BezierHandles::Cubic { handle_start, handle_end } => { if not_under_anchor(handle_start, bezier.start) { - overlay_context.line(handle_start, bezier.start, None); + overlay_context.line(handle_start, bezier.start, None, None); overlay_context.manipulator_handle(handle_start, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None); } if not_under_anchor(handle_end, bezier.end) { - overlay_context.line(handle_end, bezier.end, None); + overlay_context.line(handle_end, bezier.end, None, None); overlay_context.manipulator_handle(handle_end, is_selected(ManipulatorPointId::EndHandle(segment_id)), None); } } @@ -93,17 +93,17 @@ pub fn overlay_bezier_handle_specific_point( BezierHandles::Quadratic { handle } => { if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) { let end = if start == point_to_render { bezier.start } else { bezier.end }; - overlay_context.line(handle, end, None); + overlay_context.line(handle, end, None, None); overlay_context.manipulator_handle(handle, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None); } } BezierHandles::Cubic { handle_start, handle_end } => { if not_under_anchor(handle_start, bezier.start) && (point_to_render == start) { - overlay_context.line(handle_start, bezier.start, None); + overlay_context.line(handle_start, bezier.start, None, None); overlay_context.manipulator_handle(handle_start, is_selected(ManipulatorPointId::PrimaryHandle(segment_id)), None); } if not_under_anchor(handle_end, bezier.end) && (point_to_render == end) { - overlay_context.line(handle_end, bezier.end, None); + overlay_context.line(handle_end, bezier.end, None, None); overlay_context.manipulator_handle(handle_end, is_selected(ManipulatorPointId::EndHandle(segment_id)), None); } } diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index d13fa300..79cc7f20 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -127,11 +127,12 @@ impl OverlayContext { self.end_dpi_aware_transform(); } - pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) { - self.dashed_line(start, end, color, None, None, None) + pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option) { + self.dashed_line(start, end, color, thickness, None, None, None) } - pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option, dash_gap_width: Option, dash_offset: Option) { + #[allow(clippy::too_many_arguments)] + pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option, dash_width: Option, dash_gap_width: Option, dash_offset: Option) { self.start_dpi_aware_transform(); // Set the dash pattern @@ -159,8 +160,10 @@ impl OverlayContext { self.render_context.begin_path(); self.render_context.move_to(start.x, start.y); self.render_context.line_to(end.x, end.y); + self.render_context.set_line_width(thickness.unwrap_or(1.)); self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE)); self.render_context.stroke(); + self.render_context.set_line_width(1.); // Reset the dash pattern back to solid if dash_width.is_some() { @@ -309,8 +312,8 @@ impl OverlayContext { let end_point1 = pivot + radius * DVec2::from_angle(angle + offset_angle); let end_point2 = pivot + radius * DVec2::from_angle(offset_angle); - self.line(pivot, end_point1, Some(color_line)); - self.line(pivot, end_point2, Some(color_line)); + self.line(pivot, end_point1, Some(color_line), None); + self.line(pivot, end_point2, Some(color_line), None); self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle); } @@ -323,7 +326,7 @@ impl OverlayContext { .to_rgba_hex_srgb(); fill_color.insert(0, '#'); let fill_color = Some(fill_color.as_str()); - self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None); + self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None); self.circle(start, radius, fill_color, None); self.circle(start, radius * scale.abs(), fill_color, None); self.text( diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index a4ddb3ec..af05eea6 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -89,10 +89,7 @@ impl MessageHandler for PreferencesMessageHandler { PreferencesMessage::ModifyLayout { zoom_with_scroll } => { self.zoom_with_scroll = zoom_with_scroll; - let variant = match zoom_with_scroll { - false => MappingVariant::Default, - true => MappingVariant::ZoomWithScroll, - }; + let variant = if zoom_with_scroll { MappingVariant::ZoomWithScroll } else { MappingVariant::Default }; responses.add(KeyMappingMessage::ModifyMapping(variant)); } PreferencesMessage::SelectionMode { selection_mode } => { diff --git a/editor/src/messages/tool/common_functionality/measure.rs b/editor/src/messages/tool/common_functionality/measure.rs index cb999932..28e3de71 100644 --- a/editor/src/messages/tool/common_functionality/measure.rs +++ b/editor/src/messages/tool/common_functionality/measure.rs @@ -8,7 +8,7 @@ fn draw_dashed_line(line_start: DVec2, line_end: DVec2, transform: DAffine2, ove let min_viewport = transform.transform_point2(line_start); let max_viewport = transform.transform_point2(line_end); - overlay_context.dashed_line(min_viewport, max_viewport, None, Some(2.), Some(2.), Some(0.5)); + overlay_context.dashed_line(min_viewport, max_viewport, None, None, Some(2.), Some(2.), Some(0.5)); } /// Draws a solid line with a length annotation between two points transformed by the given affine transformations. fn draw_line_with_length(line_start: DVec2, line_end: DVec2, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext, label_alignment: LabelAlignment) { @@ -16,7 +16,7 @@ fn draw_line_with_length(line_start: DVec2, line_end: DVec2, transform: DAffine2 let min_viewport = transform.transform_point2(line_start); let max_viewport = transform.transform_point2(line_end); - overlay_context.line(min_viewport, max_viewport, None); + overlay_context.line(min_viewport, max_viewport, None, None); // Remove trailing zeros from the formatted string let length = format!("{:.2}", transform_to_document.transform_vector2(line_end - line_start).length()) diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index ac68bad6..11856589 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -81,10 +81,10 @@ impl Pivot { } } - pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, angle: f64) { + pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>) { self.recalculate_pivot(document); - if let Some(pivot) = self.pivot { - overlay_context.pivot(pivot, angle); + if let (Some(pivot), Some(data)) = (self.pivot, draw_data) { + overlay_context.pivot(pivot, data.0); } } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 561b9601..f434cdc0 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -593,10 +593,7 @@ impl ShapeState { if points_colinear_status.any(|point| first_is_colinear != point) { return ManipulatorAngle::Mixed; } - match first_is_colinear { - false => ManipulatorAngle::Free, - true => ManipulatorAngle::Colinear, - } + if first_is_colinear { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free } } pub fn convert_manipulator_handles_to_colinear(&self, vector_data: &VectorData, point_id: PointId, responses: &mut VecDeque, layer: LayerNodeIdentifier) { diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs index 4220ad73..15fab9cb 100644 --- a/editor/src/messages/tool/common_functionality/snapping.rs +++ b/editor/src/messages/tool/common_functionality/snapping.rs @@ -414,12 +414,13 @@ impl SnapManager { let start = DVec2::new(first.max().x, y); let end = DVec2::new(second.min().x, y); let signed_size = if bottom { y_size } else { -y_size }; - overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::Y * signed_size), None); - overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::Y * signed_size), None); + overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::Y * signed_size), None, None); + overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::Y * signed_size), None, None); overlay_context.line( transform.transform_point2(start + DVec2::Y * signed_size / 2.), transform.transform_point2(end + DVec2::Y * signed_size / 2.), None, + None, ); } } @@ -432,12 +433,13 @@ impl SnapManager { let start = DVec2::new(x, first.max().y); let end = DVec2::new(x, second.min().y); let signed_size = if right { x_size } else { -x_size }; - overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::X * signed_size), None); - overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::X * signed_size), None); + overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::X * signed_size), None, None); + overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::X * signed_size), None, None); overlay_context.line( transform.transform_point2(start + DVec2::X * signed_size / 2.), transform.transform_point2(end + DVec2::X * signed_size / 2.), None, + None, ); } } @@ -460,7 +462,7 @@ impl SnapManager { let align = [ind.alignment_target_x, ind.alignment_target_y].map(|target| target.map(|target| to_viewport.transform_point2(target))); let any_align = align.iter().flatten().next().is_some(); for &target in align.iter().flatten() { - overlay_context.line(viewport, target, None); + overlay_context.line(viewport, target, None, None); } for &target in align.iter().flatten() { overlay_context.manipulator_handle(target, false, None); diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index 1033c290..91abb4e4 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -374,7 +374,7 @@ pub struct BoundingBoxManager { pub bounds: [DVec2; 2], /// The transform to viewport space for the bounds co-ordinates when the bounds were last updated. pub transform: DAffine2, - /// Was the transform previously singular? + /// Whether the transform is actually singular but adjusted to not be so. pub transform_tampered: bool, /// The transform to viewport space for the bounds co-ordinates when the transformation was started. pub original_bound_transform: DAffine2, @@ -566,16 +566,24 @@ impl BoundingBoxManager { } } + pub fn render_quad(&self, overlay_context: &mut OverlayContext) { + let quad = self.transform * Quad::from_box(self.bounds); + + // Draw the bounding box rectangle + overlay_context.quad(quad, None); + } + /// Update the position of the bounding box and transform handles - pub fn render_overlays(&mut self, overlay_context: &mut OverlayContext) { + pub fn render_overlays(&mut self, overlay_context: &mut OverlayContext, render_quad: bool) { let quad = self.transform * Quad::from_box(self.bounds); let category = self.overlay_display_category(); let horizontal_edges = [quad.top_right().midpoint(quad.bottom_right()), quad.bottom_left().midpoint(quad.top_left())]; let vertical_edges = [quad.top_left().midpoint(quad.top_right()), quad.bottom_right().midpoint(quad.bottom_left())]; - // Draw the bounding box rectangle - overlay_context.quad(quad, None); + if render_quad { + self.render_quad(overlay_context); + } let mut draw_handle = |point: DVec2, angle: f64| { let quad = DAffine2::from_angle_translation(angle, point) * Quad::from_box([DVec2::splat(-RESIZE_HANDLE_SIZE / 2.), DVec2::splat(RESIZE_HANDLE_SIZE / 2.)]); diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index 80123347..f391fe34 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -60,7 +60,7 @@ pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageH let (text, font, typesetting) = get_text(layer, &document.network_interface).expect("Text layer should have text when interacting with the Text tool"); let buzz_face = font_cache.get(font).map(|data| load_face(data)); - let far = graphene_core::text::bounding_box(text, buzz_face.as_ref(), typesetting); + let far = graphene_core::text::bounding_box(text, buzz_face.as_ref(), typesetting, false); Quad::from_box([DVec2::ZERO, far]) } diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 37b18784..6e67fa68 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -231,7 +231,7 @@ impl Fsm for ArtboardToolFsmState { bounding_box_manager.bounds = bounds; bounding_box_manager.transform = document.metadata().document_to_viewport; - bounding_box_manager.render_overlays(&mut overlay_context); + bounding_box_manager.render_overlays(&mut overlay_context, true); } else { tool_data.bounding_box_manager.take(); } diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 5efb22f6..d90d6b2d 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -255,7 +255,7 @@ impl Fsm for GradientToolFsmState { let Gradient { start, end, stops, .. } = gradient; let (start, end) = (transform.transform_point2(start), transform.transform_point2(end)); - overlay_context.line(start, end, None); + overlay_context.line(start, end, None, None); overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 9006c73e..174a0dbd 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -189,7 +189,7 @@ impl Fsm for LineToolFsmState { let [viewport_start, viewport_end] = [start, end].map(|point| document.metadata().transform_to_viewport(layer).transform_point2(point)); if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { - overlay_context.line(viewport_start, viewport_end, None); + overlay_context.line(viewport_start, viewport_end, None, None); overlay_context.square(viewport_start, Some(6.), None, None); overlay_context.square(viewport_end, Some(6.), None, None); } @@ -205,6 +205,8 @@ impl Fsm for LineToolFsmState { let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); tool_data.drag_start = snapped.snapped_point_document; + responses.add(DocumentMessage::StartTransaction); + for (layer, [document_start, document_end]) in tool_data.selected_layers_with_position.iter() { let transform = document.metadata().transform_to_viewport(*layer); let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; @@ -226,8 +228,6 @@ impl Fsm for LineToolFsmState { } } - responses.add(DocumentMessage::StartTransaction); - let node_type = resolve_document_node_type("Line").expect("Line node does not exist"); let node = node_type.node_template_input_override([ None, @@ -320,9 +320,7 @@ impl Fsm for LineToolFsmState { (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); tool_data.editing_layer.take(); - if tool_data.dragging_endpoint.is_none() { - responses.add(DocumentMessage::AbortTransaction); - } + responses.add(DocumentMessage::AbortTransaction); LineToolFsmState::Ready } (_, LineToolMessage::WorkingColorChanged) => { @@ -377,12 +375,12 @@ fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: tool_data.angle = angle; + let angle_vec = DVec2::from_angle(angle); if lock_angle { - let angle_vec = DVec2::new(angle.cos(), angle.sin()); line_length = (document_points[1] - document_points[0]).dot(angle_vec); } - document_points[1] = document_points[0] + line_length * DVec2::new(angle.cos(), angle.sin()); + document_points[1] = document_points[0] + line_length * angle_vec; let constrained = snap_angle || lock_angle; let snap = &mut tool_data.snap_manager; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 4e5278de..4aac4f15 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -970,12 +970,12 @@ impl Fsm for PathToolFsmState { match axis { Axis::Y => { - overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(COLOR_OVERLAY_BLUE)); - overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(other)); + overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(other), None); } Axis::X | Axis::Both => { - overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(COLOR_OVERLAY_BLUE)); - overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(other)); + overlay_context.line(origin - DVec2::X * viewport_diagonal, origin + DVec2::X * viewport_diagonal, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(origin - DVec2::Y * viewport_diagonal, origin + DVec2::Y * viewport_diagonal, Some(other), None); } } } @@ -986,8 +986,8 @@ impl Fsm for PathToolFsmState { if let Some(closest_segment) = &tool_data.segment { overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { - overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE)); - overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE)); + overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None); overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); } diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index d3c9ad23..a09abc5b 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1272,7 +1272,7 @@ impl Fsm for PenToolFsmState { } // Draw the line between the currently-being-placed anchor and its currently-being-dragged-out outgoing handle (opposite the one currently being dragged out) - overlay_context.line(next_anchor, next_handle_start, None); + overlay_context.line(next_anchor, next_handle_start, None, None); match tool_options.pen_overlay_mode { PenOverlayMode::AllHandles => { @@ -1289,19 +1289,19 @@ impl Fsm for PenToolFsmState { if let (Some(anchor_start), Some(handle_start), Some(handle_end)) = (anchor_start, handle_start, handle_end) { // Draw the line between the most recently placed anchor and its outgoing handle (which is currently influencing the currently-being-placed segment) - overlay_context.line(anchor_start, handle_start, None); + overlay_context.line(anchor_start, handle_start, None, None); // Draw the line between the currently-being-placed anchor and its incoming handle (opposite the one currently being dragged out) - overlay_context.line(next_anchor, handle_end, None); + overlay_context.line(next_anchor, handle_end, None, None); if self == PenToolFsmState::PlacingAnchor && anchor_start != handle_start && tool_data.modifiers.lock_angle { // Draw the line between the currently-being-placed anchor and last-placed point (lock angle bent overlays) - overlay_context.dashed_line(anchor_start, next_anchor, None, Some(4.), Some(4.), Some(0.5)); + overlay_context.dashed_line(anchor_start, next_anchor, None, None, Some(4.), Some(4.), Some(0.5)); } // Draw the line between the currently-being-placed anchor and last-placed point (snap angle bent overlays) if self == PenToolFsmState::PlacingAnchor && anchor_start != handle_start && tool_data.modifiers.snap_angle { - overlay_context.dashed_line(anchor_start, next_anchor, None, Some(4.), Some(4.), Some(0.5)); + overlay_context.dashed_line(anchor_start, next_anchor, None, None, Some(4.), Some(4.), Some(0.5)); } if self == PenToolFsmState::DraggingHandle(tool_data.handle_mode) && valid(next_anchor, handle_end) { diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 00af97ab..789e5928 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -594,7 +594,7 @@ impl Fsm for SelectToolFsmState { bounding_box_manager.bounds = bounds; bounding_box_manager.transform = transform; bounding_box_manager.transform_tampered = transform_tampered; - bounding_box_manager.render_overlays(&mut overlay_context); + bounding_box_manager.render_overlays(&mut overlay_context, true); } else { tool_data.bounding_box_manager.take(); } @@ -656,7 +656,7 @@ impl Fsm for SelectToolFsmState { }); // Update pivot - tool_data.pivot.update_pivot(document, &mut overlay_context, angle); + tool_data.pivot.update_pivot(document, &mut overlay_context, Some((angle,))); // Update compass rose tool_data.compass_rose.refresh_position(document); @@ -696,7 +696,7 @@ impl Fsm for SelectToolFsmState { &format!("#{}", color_string) }; let line_center = tool_data.line_center; - overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color)); + overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None); } } } @@ -721,8 +721,8 @@ impl Fsm for SelectToolFsmState { let edge = DVec2::from_angle(snapped_angle) * viewport_diagonal; let perp = edge.perp(); - overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(COLOR_OVERLAY_BLUE)); - overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(other)); + overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(COLOR_OVERLAY_BLUE), None); + overlay_context.line(origin - perp * viewport_diagonal, origin + perp * viewport_diagonal, Some(other), None); } // Check if the tool is in selection mode @@ -1269,18 +1269,6 @@ impl Fsm for SelectToolFsmState { state } - (SelectToolFsmState::Dragging { .. }, SelectToolMessage::Enter) => { - let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON { - true => DocumentMessage::AbortTransaction, - false => DocumentMessage::EndTransaction, - }; - tool_data.axis_align = false; - tool_data.snap_manager.cleanup(responses); - responses.add_front(response); - - let selection = tool_data.nested_selection_behavior; - SelectToolFsmState::Ready { selection } - } (SelectToolFsmState::Dragging { has_dragged, .. }, SelectToolMessage::DragStop { remove_from_selection }) => { // Deselect layer if not snap dragging responses.add(DocumentMessage::EndTransaction); @@ -1337,48 +1325,25 @@ impl Fsm for SelectToolFsmState { let selection = tool_data.nested_selection_behavior; SelectToolFsmState::Ready { selection } } - (SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => { - let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON { - true => DocumentMessage::AbortTransaction, - false => DocumentMessage::EndTransaction, - }; + ( + SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds | SelectToolFsmState::Dragging { .. }, + SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter, + ) => { + let drag_too_small = input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON; + let response = if drag_too_small { DocumentMessage::AbortTransaction } else { DocumentMessage::EndTransaction }; responses.add(response); - + tool_data.axis_align = false; tool_data.snap_manager.cleanup(responses); - if let Some(bounds) = &mut tool_data.bounding_box_manager { - bounds.original_transforms.clear(); + if !matches!(self, SelectToolFsmState::DraggingPivot) { + if let Some(bounds) = &mut tool_data.bounding_box_manager { + bounds.original_transforms.clear(); + } } let selection = tool_data.nested_selection_behavior; SelectToolFsmState::Ready { selection } } - (SelectToolFsmState::RotatingBounds, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => { - let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON { - true => DocumentMessage::AbortTransaction, - false => DocumentMessage::EndTransaction, - }; - responses.add(response); - - if let Some(bounds) = &mut tool_data.bounding_box_manager { - bounds.original_transforms.clear(); - } - - let selection = tool_data.nested_selection_behavior; - SelectToolFsmState::Ready { selection } - } - (SelectToolFsmState::DraggingPivot, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => { - let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON { - true => DocumentMessage::AbortTransaction, - false => DocumentMessage::EndTransaction, - }; - responses.add(response); - - tool_data.snap_manager.cleanup(responses); - - let selection = tool_data.nested_selection_behavior; - SelectToolFsmState::Ready { selection } - } (SelectToolFsmState::Drawing { selection_shape, .. }, SelectToolMessage::DragStop { remove_from_selection }) => { let quad = tool_data.selection_quad(); diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index 87526192..b1c5fe5a 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -203,6 +203,7 @@ impl<'a> MessageHandler> for TextToo match self.fsm_state { TextToolFsmState::Ready => actions!(TextToolMessageDiscriminant; DragStart, + PointerOutsideViewport, PointerMove, ), TextToolFsmState::Editing => actions!(TextToolMessageDiscriminant; @@ -213,11 +214,13 @@ impl<'a> MessageHandler> for TextToo DragStop, Abort, PointerMove, + PointerOutsideViewport, ), TextToolFsmState::ResizingBounds => actions!(TextToolMessageDiscriminant; DragStop, Abort, PointerMove, + PointerOutsideViewport, ), } } @@ -242,9 +245,9 @@ enum TextToolFsmState { Ready, /// The user is typing in the interactive viewport text area. Editing, - /// The user is clicking to add a new text layer, but hasn't dragged or released the left mouse button yet. - Placing, /// The user is dragging to create a new text area. + Placing, + /// The user is dragging an existing text layer to move it. Dragging, /// The user is dragging to resize the text area. ResizingBounds, @@ -271,6 +274,8 @@ struct TextToolData { layer: LayerNodeIdentifier, editing_text: Option, new_text: String, + drag_start: DVec2, + drag_current: DVec2, resize: Resize, auto_panning: AutoPanning, // Since the overlays must be drawn without knowledge of the inputs @@ -464,7 +469,7 @@ impl Fsm for TextToolFsmState { }); if let Some(editing_text) = tool_data.editing_text.as_mut() { let buzz_face = font_cache.get(&editing_text.font).map(|data| load_face(data)); - let far = graphene_core::text::bounding_box(&tool_data.new_text, buzz_face.as_ref(), editing_text.typesetting); + let far = graphene_core::text::bounding_box(&tool_data.new_text, buzz_face.as_ref(), editing_text.typesetting, false); if far.x != 0. && far.y != 0. { let quad = Quad::from_box([DVec2::ZERO, far]); let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad; @@ -475,7 +480,7 @@ impl Fsm for TextToolFsmState { TextToolFsmState::Editing } (_, TextToolMessage::Overlays(mut overlay_context)) => { - if matches!(self, Self::Placing | Self::Dragging) { + if matches!(self, Self::Placing) { // Get the updated selection box bounds let quad = Quad::from_box(tool_data.cached_resize_bounds); @@ -506,21 +511,18 @@ impl Fsm for TextToolFsmState { bounding_box_manager.bounds = [bounds.0[0], bounds.0[2]]; bounding_box_manager.transform = layer_transform; - bounding_box_manager.render_overlays(&mut overlay_context); - + bounding_box_manager.render_quad(&mut overlay_context); // Draw red overlay if text is clipped let transformed_quad = layer_transform * bounds; if let Some((text, font, typesetting)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { let buzz_face = font_cache.get(font).map(|data| load_face(data)); if lines_clipping(text.as_str(), buzz_face, typesetting) { - overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED)); + overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.)); } } - // The angle is choosen to be parallel to the X axis in the bounds transform. - let angle = bounding_box_manager.transform.transform_vector2(DVec2::X).to_angle(); - // Update pivot - tool_data.pivot.update_pivot(document, &mut overlay_context, angle); + bounding_box_manager.render_overlays(&mut overlay_context, false); + tool_data.pivot.update_pivot(document, &mut overlay_context, None); } else { tool_data.bounding_box_manager.take(); } @@ -540,6 +542,8 @@ impl Fsm for TextToolFsmState { (TextToolFsmState::Ready, TextToolMessage::DragStart) => { tool_data.resize.start(document, input); tool_data.cached_resize_bounds = [tool_data.resize.viewport_drag_start(document); 2]; + tool_data.drag_start = input.mouse.position; + tool_data.drag_current = input.mouse.position; let dragging_bounds = tool_data.bounding_box_manager.as_mut().and_then(|bounding_box| { let edges = bounding_box.check_selected_edges(input.mouse.position); @@ -557,7 +561,7 @@ impl Fsm for TextToolFsmState { let mut all_selected = selected.selected_visible_and_unlocked_layers(&document.network_interface); let selected = all_selected.find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, "Text")); - if let Some(_selected_edges) = dragging_bounds { + if dragging_bounds.is_some() { responses.add(DocumentMessage::StartTransaction); // Set the original transform @@ -568,12 +572,25 @@ impl Fsm for TextToolFsmState { if let Some(bounds) = &mut tool_data.bounding_box_manager { bounds.original_bound_transform = bounds.transform; - bounds.center_of_transformation = bounds.transform.transform_point2((bounds.bounds[0] + bounds.bounds[1]) / 2.); } tool_data.get_snap_candidates(document, font_cache); return TextToolFsmState::ResizingBounds; + } else if let Some(clicked_layer) = TextToolData::check_click(document, input, font_cache) { + responses.add(DocumentMessage::StartTransaction); + + if selected != Some(clicked_layer) { + responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![clicked_layer.to_node()] }); + } + + let original_transform = document.metadata().transform_to_document(clicked_layer); + tool_data.layer_dragging = Some(ResizingLayer { + id: clicked_layer, + original_transform, + }); + tool_data.get_snap_candidates(document, font_cache); + return TextToolFsmState::Dragging; } TextToolFsmState::Placing } @@ -596,7 +613,7 @@ impl Fsm for TextToolFsmState { TextToolFsmState::Ready } - (Self::Placing | TextToolFsmState::Dragging, TextToolMessage::PointerMove { center, lock_ratio }) => { + (TextToolFsmState::Placing, TextToolMessage::PointerMove { center, lock_ratio }) => { tool_data.cached_resize_bounds = tool_data.resize.calculate_points_ignore_layer(document, input, center, lock_ratio, false); responses.add(OverlaysMessage::Draw); @@ -608,18 +625,42 @@ impl Fsm for TextToolFsmState { ]; tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); + TextToolFsmState::Placing + } + (TextToolFsmState::Dragging, TextToolMessage::PointerMove { center, lock_ratio }) => { + if let Some(dragging_layer) = &tool_data.layer_dragging { + let delta = input.mouse.position - tool_data.drag_current; + tool_data.drag_current = input.mouse.position; + + responses.add(GraphOperationMessage::TransformChange { + layer: dragging_layer.id, + transform: DAffine2::from_translation(delta), + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + + responses.add(NodeGraphMessage::RunDocumentGraph); + + // Auto-panning + let messages = [ + TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(), + TextToolMessage::PointerMove { center, lock_ratio }.into(), + ]; + tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); + } + TextToolFsmState::Dragging } (TextToolFsmState::ResizingBounds, TextToolMessage::PointerMove { center, lock_ratio }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { if let Some(movement) = &mut bounds.selected_edges { - let (center_bool, lock_ratio_bool) = (input.keyboard.key(center), input.keyboard.key(lock_ratio)); - let center_position = center_bool.then_some(bounds.center_of_transformation); + let (centered, constrain) = (input.keyboard.key(center), input.keyboard.key(lock_ratio)); + let center_position = centered.then_some(bounds.center_of_transformation); let Some(dragging_layer) = tool_data.layer_dragging else { return TextToolFsmState::Ready }; let Some(node_id) = graph_modification_utils::get_text_id(dragging_layer.id, &document.network_interface) else { warn!("Cannot get text node id"); - tool_data.layer_dragging = None; + tool_data.layer_dragging.take(); return TextToolFsmState::Ready; }; @@ -630,7 +671,7 @@ impl Fsm for TextToolFsmState { snap_data: SnapData::ignore(document, input, &selected), }); - let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, lock_ratio_bool, snap); + let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, constrain, snap); // Normalize so the size is always positive let (position, size) = (position.min(position + size), size.abs()); @@ -677,13 +718,13 @@ impl Fsm for TextToolFsmState { self } - (TextToolFsmState::Placing | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => { + (TextToolFsmState::Placing, TextToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning setup let _ = tool_data.auto_panning.shift_viewport(input, responses); - TextToolFsmState::Dragging + TextToolFsmState::Placing } - (TextToolFsmState::ResizingBounds, TextToolMessage::PointerOutsideViewport { .. }) => { + (TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => { // AutoPanning if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) { if let Some(bounds) = &mut tool_data.bounding_box_manager { @@ -705,10 +746,8 @@ impl Fsm for TextToolFsmState { state } (TextToolFsmState::ResizingBounds, TextToolMessage::DragStop) => { - let response = match input.mouse.position.distance(tool_data.resize.viewport_drag_start(document)) < 10. * f64::EPSILON { - true => DocumentMessage::AbortTransaction, - false => DocumentMessage::EndTransaction, - }; + let drag_too_small = input.mouse.position.distance(tool_data.resize.viewport_drag_start(document)) < 10. * f64::EPSILON; + let response = if drag_too_small { DocumentMessage::AbortTransaction } else { DocumentMessage::EndTransaction }; responses.add(response); tool_data.resize.snap_manager.cleanup(responses); @@ -719,7 +758,7 @@ impl Fsm for TextToolFsmState { TextToolFsmState::Ready } - (TextToolFsmState::Placing | TextToolFsmState::Dragging, TextToolMessage::DragStop) => { + (TextToolFsmState::Placing, TextToolMessage::DragStop) => { let [start, end] = tool_data.cached_resize_bounds; let has_dragged = (start - end).length_squared() > DRAG_THRESHOLD * DRAG_THRESHOLD; @@ -749,6 +788,27 @@ impl Fsm for TextToolFsmState { tool_data.new_text(document, editing_text, font_cache, responses); TextToolFsmState::Editing } + (TextToolFsmState::Dragging, TextToolMessage::DragStop) => { + let drag_too_small = input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON; + let response = if drag_too_small { DocumentMessage::AbortTransaction } else { DocumentMessage::EndTransaction }; + responses.add(response); + + tool_data.resize.snap_manager.cleanup(responses); + + if let Some(bounds) = &mut tool_data.bounding_box_manager { + bounds.original_transforms.clear(); + } + + if drag_too_small { + if let Some(layer_info) = &tool_data.layer_dragging { + tool_data.start_editing_layer(layer_info.id, self, document, font_cache, responses); + return TextToolFsmState::Editing; + } + } + tool_data.layer_dragging.take(); + + TextToolFsmState::Ready + } (TextToolFsmState::Editing, TextToolMessage::TextChange { new_text, is_left_or_right_click }) => { tool_data.new_text = new_text; @@ -789,16 +849,21 @@ impl Fsm for TextToolFsmState { } responses.add(FrontendMessage::TriggerTextCommit); - TextToolFsmState::Editing } (state, TextToolMessage::Abort) => { - input.mouse.finish_transaction(tool_data.resize.viewport_drag_start(document), responses); - tool_data.resize.cleanup(responses); - - if state == TextToolFsmState::Editing { - tool_data.set_editing(false, font_cache, responses); + if matches!(state, TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging) { + responses.add(DocumentMessage::AbortTransaction); + if let Some(bounds) = &mut tool_data.bounding_box_manager { + bounds.original_transforms.clear(); + } + if matches!(state, TextToolFsmState::Dragging) { + tool_data.layer_dragging.take(); + } + } else { + input.mouse.finish_transaction(tool_data.resize.viewport_drag_start(document), responses); } + tool_data.resize.cleanup(responses); TextToolFsmState::Ready } @@ -821,12 +886,13 @@ impl Fsm for TextToolFsmState { HintInfo::keys([Key::Control, Key::Enter], "").add_mac_keys([Key::Command, Key::Enter]), HintInfo::keys([Key::Escape], "Commit Changes").prepend_slash(), ])]), - TextToolFsmState::Placing | TextToolFsmState::Dragging => HintData(vec![ + TextToolFsmState::Placing => HintData(vec![ HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]), ]), + TextToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]), TextToolFsmState::ResizingBounds => HintData(vec![ - HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Resize Text Box")]), + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo::keys([Key::Shift], "Lock Aspect Ratio"), HintInfo::keys([Key::Alt], "From Center")]), ]), }; @@ -836,7 +902,7 @@ impl Fsm for TextToolFsmState { fn update_cursor(&self, responses: &mut VecDeque) { let cursor = match self { - TextToolFsmState::Dragging => MouseCursorIcon::Crosshair, + TextToolFsmState::Placing => MouseCursorIcon::Crosshair, _ => MouseCursorIcon::Text, }; responses.add(FrontendMessage::UpdateMouseCursor { cursor }); diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index cf4c4585..03082f43 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -242,7 +242,7 @@ impl MessageHandler> for TransformLayer if matches!(axis_constraint, Axis::Both | Axis::X) && translation.x != 0. { let end = if self.local { (quad[1] - quad[0]).rotate(e1) + quad[0] } else { quad[1] }; - overlay_context.line(quad[0], end, None); + overlay_context.line(quad[0], end, None, None); let x_transform = DAffine2::from_translation((quad[0] + end) / 2.); overlay_context.text(&format_rounded(translation.x, 3), COLOR_OVERLAY_BLUE, None, x_transform, 4., [Pivot::Middle, Pivot::End]); @@ -250,7 +250,7 @@ impl MessageHandler> for TransformLayer if matches!(axis_constraint, Axis::Both | Axis::Y) && translation.y != 0. { let end = if self.local { (quad[3] - quad[0]).rotate(e1) + quad[0] } else { quad[3] }; - overlay_context.line(quad[0], end, None); + overlay_context.line(quad[0], end, None, None); let x_parameter = viewport_translate.x.clamp(-1., 1.); let y_transform = DAffine2::from_translation((quad[0] + end) / 2. + x_parameter * DVec2::X * 0.); let pivot_selection = if x_parameter >= -1e-3 { Pivot::Start } else { Pivot::End }; @@ -259,8 +259,8 @@ impl MessageHandler> for TransformLayer } } if matches!(axis_constraint, Axis::Both) && translation.x != 0. && translation.y != 0. { - overlay_context.dashed_line(quad[1], quad[2], None, Some(2.), Some(2.), Some(0.5)); - overlay_context.dashed_line(quad[3], quad[2], None, Some(2.), Some(2.), Some(0.5)); + overlay_context.dashed_line(quad[1], quad[2], None, None, Some(2.), Some(2.), Some(0.5)); + overlay_context.dashed_line(quad[3], quad[2], None, None, Some(2.), Some(2.), Some(0.5)); } } TransformOperation::Scaling(scale) => { @@ -274,9 +274,9 @@ impl MessageHandler> for TransformLayer let end_point = pivot + local_edge * scale.max(1.); if scale > 0. { - overlay_context.dashed_line(pivot, boundary_point, None, Some(4.), Some(4.), Some(0.5)); + overlay_context.dashed_line(pivot, boundary_point, None, None, Some(4.), Some(4.), Some(0.5)); } - overlay_context.line(boundary_point, end_point, None); + overlay_context.line(boundary_point, end_point, None, None); let transform = DAffine2::from_translation(boundary_point.midpoint(pivot) + local_edge.perp().normalize_or(DVec2::X) * local_edge.element_product().signum() * 24.); overlay_context.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]); diff --git a/libraries/bezier-rs/src/subpath/mod.rs b/libraries/bezier-rs/src/subpath/mod.rs index e299afd8..c349e01e 100644 --- a/libraries/bezier-rs/src/subpath/mod.rs +++ b/libraries/bezier-rs/src/subpath/mod.rs @@ -56,11 +56,7 @@ impl Iterator for SubpathIter<'_, PointId> { return None; } let closed = if self.is_always_closed { true } else { self.subpath.closed }; - let len = self.subpath.len() - 1 - + match closed { - true => 1, - false => 0, - }; + let len = self.subpath.len() - 1 + if closed { 1 } else { 0 }; if self.index >= len { return None; } diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 3dc9f6a2..785c5b7b 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -299,19 +299,13 @@ fn absolute_value(_: impl Ctx, #[implementations(f6 /// The minimum function (min) picks the smaller of two numbers. #[node_macro::node(category("Math: Numeric"))] fn min(_: impl Ctx, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] value: T, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] other_value: T) -> T { - match value < other_value { - true => value, - false => other_value, - } + if value < other_value { value } else { other_value } } /// The maximum function (max) picks the larger of two numbers. #[node_macro::node(category("Math: Numeric"))] fn max(_: impl Ctx, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] value: T, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] other_value: T) -> T { - match value > other_value { - true => value, - false => other_value, - } + if value > other_value { value } else { other_value } } /// The clamp function (clamp) restricts a number to a specified range between a minimum and maximum value. The minimum and maximum values are automatically swapped if they are reversed. diff --git a/node-graph/gcore/src/text/to_path.rs b/node-graph/gcore/src/text/to_path.rs index a238b483..4a0e13cb 100644 --- a/node-graph/gcore/src/text/to_path.rs +++ b/node-graph/gcore/src/text/to_path.rs @@ -7,7 +7,7 @@ use rustybuzz::{GlyphBuffer, UnicodeBuffer}; struct Builder { current_subpath: Subpath, other_subpaths: Vec>, - pos: DVec2, + text_cursor: DVec2, offset: DVec2, ascender: f64, scale: f64, @@ -16,7 +16,7 @@ struct Builder { impl Builder { fn point(&self, x: f32, y: f32) -> DVec2 { - self.pos + self.offset + DVec2::new(x as f64, self.ascender - y as f64) * self.scale + self.text_cursor + self.offset + DVec2::new(x as f64, self.ascender - y as f64) * self.scale } } @@ -99,11 +99,7 @@ impl Default for TypesettingConfig { } pub fn to_path(str: &str, buzz_face: Option, typesetting: TypesettingConfig) -> Vec> { - let buzz_face = match buzz_face { - Some(face) => face, - // Show blank layer if font has not loaded - None => return vec![], - }; + let Some(buzz_face) = buzz_face else { return vec![] }; let space_glyph = buzz_face.glyph_index(' '); let (scale, line_height, mut buffer) = font_properties(&buzz_face, typesetting.font_size, typesetting.line_height_ratio); @@ -111,7 +107,7 @@ pub fn to_path(str: &str, buzz_face: Option, typesetting: Types let mut builder = Builder { current_subpath: Subpath::new(Vec::new(), false), other_subpaths: Vec::new(), - pos: DVec2::ZERO, + text_cursor: DVec2::ZERO, offset: DVec2::ZERO, ascender: (buzz_face.ascender() as f64 / buzz_face.height() as f64) * typesetting.font_size / scale, scale, @@ -124,19 +120,19 @@ pub fn to_path(str: &str, buzz_face: Option, typesetting: Types let glyph_buffer = rustybuzz::shape(&buzz_face, &[], buffer); // Don't wrap the first word - if index != 0 && wrap_word(typesetting.max_width, &glyph_buffer, scale, typesetting.character_spacing, builder.pos.x, space_glyph) { - builder.pos = DVec2::new(0., builder.pos.y + line_height); + if index != 0 && wrap_word(typesetting.max_width, &glyph_buffer, scale, typesetting.character_spacing, builder.text_cursor.x, space_glyph) { + builder.text_cursor = DVec2::new(0., builder.text_cursor.y + line_height); } for (glyph_position, glyph_info) in glyph_buffer.glyph_positions().iter().zip(glyph_buffer.glyph_infos()) { let glyph_id = GlyphId(glyph_info.glyph_id as u16); if let Some(max_width) = typesetting.max_width { - if space_glyph != Some(glyph_id) && builder.pos.x + (glyph_position.x_advance as f64 * builder.scale * typesetting.character_spacing) >= max_width { - builder.pos = DVec2::new(0., builder.pos.y + line_height); + if space_glyph != Some(glyph_id) && builder.text_cursor.x + (glyph_position.x_advance as f64 * builder.scale * typesetting.character_spacing) >= max_width { + builder.text_cursor = DVec2::new(0., builder.text_cursor.y + line_height); } } // Clip when the height is exceeded - if typesetting.max_height.is_some_and(|max_height| builder.pos.y > max_height - line_height) { + if typesetting.max_height.is_some_and(|max_height| builder.text_cursor.y > max_height - line_height) { return builder.other_subpaths; } @@ -146,30 +142,31 @@ pub fn to_path(str: &str, buzz_face: Option, typesetting: Types builder.other_subpaths.push(core::mem::replace(&mut builder.current_subpath, Subpath::new(Vec::new(), false))); } - builder.pos += DVec2::new(glyph_position.x_advance as f64 * typesetting.character_spacing, glyph_position.y_advance as f64) * builder.scale; + builder.text_cursor += DVec2::new(glyph_position.x_advance as f64 * typesetting.character_spacing, glyph_position.y_advance as f64) * builder.scale; } buffer = glyph_buffer.clear(); } - builder.pos = DVec2::new(0., builder.pos.y + line_height); + builder.text_cursor = DVec2::new(0., builder.text_cursor.y + line_height); } builder.other_subpaths } -pub fn bounding_box(str: &str, buzz_face: Option<&rustybuzz::Face>, typesetting: TypesettingConfig) -> DVec2 { - let buzz_face = match buzz_face { - Some(face) => face, - // Show blank layer if font has not loaded - None => return DVec2::ZERO, - }; +pub fn bounding_box(str: &str, buzz_face: Option<&rustybuzz::Face>, typesetting: TypesettingConfig, for_clipping_test: bool) -> DVec2 { + // Show blank layer if font has not loaded + let Some(buzz_face) = buzz_face else { return DVec2::ZERO }; let space_glyph = buzz_face.glyph_index(' '); let (scale, line_height, mut buffer) = font_properties(buzz_face, typesetting.font_size, typesetting.line_height_ratio); - let mut pos = DVec2::ZERO; - let mut bounds = DVec2::ZERO; + let [mut text_cursor, mut bounds] = [DVec2::ZERO; 2]; + if !for_clipping_test { + if let (Some(max_height), Some(max_width)) = (typesetting.max_height, typesetting.max_width) { + return DVec2::new(max_width, max_height); + } + } for line in str.split('\n') { for (index, word) in SplitWordsIncludingSpaces::new(line).enumerate() { @@ -178,32 +175,34 @@ pub fn bounding_box(str: &str, buzz_face: Option<&rustybuzz::Face>, typesetting: let glyph_buffer = rustybuzz::shape(buzz_face, &[], buffer); // Don't wrap the first word - if index != 0 && wrap_word(typesetting.max_width, &glyph_buffer, scale, typesetting.character_spacing, pos.x, space_glyph) { - pos = DVec2::new(0., pos.y + line_height); + if index != 0 && wrap_word(typesetting.max_width, &glyph_buffer, scale, typesetting.character_spacing, text_cursor.x, space_glyph) { + text_cursor = DVec2::new(0., text_cursor.y + line_height); } for (glyph_position, glyph_info) in glyph_buffer.glyph_positions().iter().zip(glyph_buffer.glyph_infos()) { let glyph_id = GlyphId(glyph_info.glyph_id as u16); if let Some(max_width) = typesetting.max_width { - if space_glyph != Some(glyph_id) && pos.x + (glyph_position.x_advance as f64 * scale * typesetting.character_spacing) >= max_width { - pos = DVec2::new(0., pos.y + line_height); + if space_glyph != Some(glyph_id) && text_cursor.x + (glyph_position.x_advance as f64 * scale * typesetting.character_spacing) >= max_width { + text_cursor = DVec2::new(0., text_cursor.y + line_height); } } - pos += DVec2::new(glyph_position.x_advance as f64 * typesetting.character_spacing, glyph_position.y_advance as f64) * scale; - bounds = bounds.max(pos + DVec2::new(0., line_height)); + text_cursor += DVec2::new(glyph_position.x_advance as f64 * typesetting.character_spacing, glyph_position.y_advance as f64) * scale; + bounds = bounds.max(text_cursor + DVec2::new(0., line_height)); } buffer = glyph_buffer.clear(); } - pos = DVec2::new(0., pos.y + line_height); - bounds = bounds.max(pos); + text_cursor = DVec2::new(0., text_cursor.y + line_height); + bounds = bounds.max(text_cursor); } - if let Some(max_width) = typesetting.max_width { - bounds.x = max_width; - } - if let Some(max_height) = typesetting.max_height { - bounds.y = max_height; + if !for_clipping_test { + if let Some(max_width) = typesetting.max_width { + bounds.x = max_width; + } + if let Some(max_height) = typesetting.max_height { + bounds.y = max_height; + } } bounds @@ -214,56 +213,9 @@ pub fn load_face(data: &[u8]) -> rustybuzz::Face { } pub fn lines_clipping(str: &str, buzz_face: Option, typesetting: TypesettingConfig) -> bool { - let buzz_face = match buzz_face { - Some(face) => face, - // False if font hasn't loaded - None => return false, - }; - - if typesetting.max_height.is_none() { - return false; - } - - let space_glyph = buzz_face.glyph_index(' '); - - let (scale, line_height, mut buffer) = font_properties(&buzz_face, typesetting.font_size, typesetting.line_height_ratio); - - let mut pos = DVec2::ZERO; - let mut bounds = DVec2::ZERO; - - for line in str.split('\n') { - for (index, word) in SplitWordsIncludingSpaces::new(line).enumerate() { - push_str(&mut buffer, word); - - let glyph_buffer = rustybuzz::shape(&buzz_face, &[], buffer); - - // Don't wrap the first word - if index != 0 && wrap_word(typesetting.max_width, &glyph_buffer, scale, typesetting.character_spacing, pos.x, space_glyph) { - pos = DVec2::new(0., pos.y + line_height); - } - - for (glyph_position, glyph_info) in glyph_buffer.glyph_positions().iter().zip(glyph_buffer.glyph_infos()) { - let glyph_id = GlyphId(glyph_info.glyph_id as u16); - if let Some(max_width) = typesetting.max_width { - if space_glyph != Some(glyph_id) && pos.x + (glyph_position.x_advance as f64 * scale * typesetting.character_spacing) >= max_width { - pos = DVec2::new(0., pos.y + line_height); - } - } - pos += DVec2::new(glyph_position.x_advance as f64 * typesetting.character_spacing, glyph_position.y_advance as f64) * scale; - bounds = bounds.max(pos + DVec2::new(0., line_height)); - } - - buffer = glyph_buffer.clear(); - } - pos = DVec2::new(0., pos.y + line_height); - bounds = bounds.max(pos); - } - - if typesetting.max_height.unwrap() < bounds.y { - return true; - } - - false + let Some(max_height) = typesetting.max_height else { return false }; + let bounds = bounding_box(str, buzz_face.as_ref(), typesetting, true); + max_height < bounds.y } struct SplitWordsIncludingSpaces<'a> {