Repository: GraphiteEditor/Graphite
File: editor/src/messages/tool/common_functionality/shape_editor.rs (Rust)

Code before:
				return;
			};
			let new_position = non_zero_handle.length(vector_data) * direction * -1.;
			let modification_type = zero_handle.set_relative_position(new_position);
			responses.add(GraphOperationMessage::Vector { layer, modification_type });
		} else {
			// Push both in and out handles into the correct position
			for ((handle, sign), other_anchor) in handles.iter().zip([1., -1.]).zip(&anchor_positions) {
				// To find the length of the new tangent we just take the distance to the anchor and divide by 3 (pretty arbitrary)
				let Some(length) = other_anchor.map(|position| (position - anchor_position).length() / 3.) else {
					continue;
				};
				let new_position = handle_direction * length * sign;
				let modification_type = handle.set_relative_position(new_position);
				responses.add(GraphOperationMessage::Vector { layer, modification_type });

				// Create the opposite handle if it doesn't exist (if it is not a cubic segment)
				if handle.opposite().to_manipulator_point().get_position(vector_data).is_none() {
					let modification_type = handle.opposite().set_relative_position(DVec2::ZERO);
					responses.add(GraphOperationMessage::Vector { layer, modification_type });
				}
			}
		}
	}

	/// Converts all selected points to colinear while moving the handles to ensure their 180° angle separation.
	/// If only one handle is selected, the other handle will be moved to match the angle of the selected handle.
	/// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths.
	/// Assumes all selected manipulators have handles that are already not colinear.
	pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler) {
		let mut skip_set = HashSet::new();

		for (&layer, layer_state) in self.selected_shape_state.iter() {
			let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
				continue;
			};
			let transform = document.metadata().transform_to_document(layer);

			for &point in layer_state.selected_points.iter() {
				let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
				if skip_set.contains(&handles) || skip_set.contains(&[handles[1], handles[0]]) {
					continue;
				};

				skip_set.insert(handles);

				let [selected0, selected1] = handles.map(|handle| layer_state.selected_points.contains(&handle.to_manipulator_point()));
				let handle_positions = handles.map(|handle| handle.to_manipulator_point().get_position(&vector_data));

				let Some(anchor_id) = point.get_anchor(&vector_data) else { continue };
				let Some(anchor) = vector_data.point_domain.position_from_id(anchor_id) else { continue };

Review comment (refactor):
Does the formatter let you remove this `continue`'s semicolon to put it all on one line, or is it too long?

Code after:
		{
			handle_direction *= -1.;
		}

		if non_zero_handles != 0 {
			let [a, b] = handles.as_slice() else { return };
			let (non_zero_handle, zero_handle) = if a.length(vector_data) > 1e-6 { (a, b) } else { (b, a) };
			let Some(direction) = non_zero_handle
				.to_manipulator_point()
				.get_position(&vector_data)
				.and_then(|position| (position - anchor_position).try_normalize())
			else {
				return;
			};
			let new_position = -direction * non_zero_handle.length(vector_data);
			let modification_type = zero_handle.set_relative_position(new_position);
			responses.add(GraphOperationMessage::Vector { layer, modification_type });
		} else {
			// Push both in and out handles into the correct position
			for ((handle, sign), other_anchor) in handles.iter().zip([1., -1.]).zip(&anchor_positions) {
				let Some(anchor_vector) = other_anchor.map(|position| (position - anchor_position)) else {
					continue;
				};

				let Some(unit_vector) = anchor_vector.try_normalize() else {
					continue;
				};

				let projection = anchor_vector.length() * HANDLE_LENGTH_FACTOR * handle_direction.dot(unit_vector).abs();

				let new_position = handle_direction * projection * sign;
				let modification_type = handle.set_relative_position(new_position);
				responses.add(GraphOperationMessage::Vector { layer, modification_type });

				// Create the opposite handle if it doesn't exist (if it is not a cubic segment)
				if handle.opposite().to_manipulator_point().get_position(vector_data).is_none() {
					let modification_type = handle.opposite().set_relative_position(DVec2::ZERO);
					responses.add(GraphOperationMessage::Vector { layer, modification_type });
				}
			}
		}
	}

	/// Converts all selected points to colinear while moving the handles to ensure their 180° angle separation.
	/// If only one handle is selected, the other handle will be moved to match the angle of the selected handle.
	/// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths.
	/// Assumes all selected manipulators have handles that are already not colinear.
	pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler) {
		let mut skip_set = HashSet::new();

		for (&layer, layer_state) in self.selected_shape_state.iter() {