Repository: godotengine/godot
File: editor/plugins/path_3d_editor_plugin.cpp (C++)

Code before:
		if (p_cancel) {
			c->set_point_position(p_id, p_restore);
			return;
		}
		ur->create_action(TTR("Set Curve Point Position"));
		ur->add_do_method(c.ptr(), "set_point_position", p_id, c->get_point_position(p_id));
		ur->add_undo_method(c.ptr(), "set_point_position", p_id, p_restore);
		ur->commit_action();

		return;
	}

	// (p_id + 1) Accounts for the first point only having an "out" handle
	int idx = (p_id + 1) / 2;

	if (_is_in_handle(p_id, c->get_point_count())) {
		if (p_cancel) {
			c->set_point_in(p_id, p_restore);
			return;
		}

		ur->create_action(TTR("Set Curve In Position"));
		ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
		ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);

		if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) {
			ur->add_do_method(c.ptr(), "set_point_out", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length));
			ur->add_undo_method(c.ptr(), "set_point_out", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_out_length));
		}
		ur->commit_action();

	} else {
		if (p_cancel) {
			c->set_point_out(idx, p_restore);

			return;
		}

		ur->create_action(TTR("Set Curve Out Position"));
		ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
		ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);

		if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) {
			ur->add_do_method(c.ptr(), "set_point_in", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length));
			ur->add_undo_method(c.ptr(), "set_point_in", idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_in_length));
		}
		ur->commit_action();
	}
}

void Path3DGizmo::redraw() {

Review comment (suggestion):
```suggestion
					tilt_angle = Math::rad_to_deg(tilt_angle) + snap * 0.5; // Else it won't reach +180.
```

Code after:
					real_t snap = Node3DEditor::get_singleton()->get_rotate_snap();

					tilt_angle = Math::rad_to_deg(tilt_angle) + snap * 0.5; // Else it won't reach +180.
					tilt_angle -= Math::fmod(tilt_angle, snap);
					tilt_angle = Math::deg_to_rad(tilt_angle);
				}

				c->set_point_tilt(idx, tilt_angle);
			}
			break;
		}
	}
}

void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
	Ref<Curve3D> c = path->get_curve();
	if (c.is_null()) {
		return;
	}

	EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();

	// Primary handles: position.
	if (!p_secondary) {
		// Special cas for primary handle, the handle id equals control point id.
		const int idx = p_id;
		if (p_cancel) {
			c->set_point_position(idx, p_restore);
			return;
		}
		ur->create_action(TTR("Set Curve Point Position"));
		ur->add_do_method(c.ptr(), "set_point_position", idx, c->get_point_position(idx));
		ur->add_undo_method(c.ptr(), "set_point_position", idx, p_restore);
		ur->commit_action();

		return;
	}

	// Secondary handles: in, out, tilt.
	const HandleInfo info = _secondary_handles_info[p_id];
	const int idx = info.point_idx;
	switch (info.type) {
		case HandleType::HANDLE_TYPE_OUT: {
			if (p_cancel) {
				c->set_point_out(idx, p_restore);

				return;
			}

			ur->create_action(TTR("Set Curve Out Position"));
			ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));