ronantakizawa/codereview-bench huggingface dataset:
Repository: godotengine/godot
File: modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs (C#)

Code before:
using System;

namespace Godot
{
    [AttributeUsage(AttributeTargets.Method)]
    public class AnyPeerAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Method)]
    public class AuthorityAttribute : Attribute { }
}


Review comment (security):
This definition seems to be describing the `AttributeUsage` attribute instead of the `AuthorityAttribute`.

Code after:
using System;

namespace Godot
{
    /// <summary>
    /// Constructs a new AnyPeerAttribute instance. Members with the AnyPeerAttribute are given authority over their own player.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class AnyPeerAttribute : Attribute { }

    /// <summary>
    /// Constructs a new AuthorityAttribute instance. Members with the AuthorityAttribute are given authority over the game.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class AuthorityAttribute : Attribute { }
}
Repository: godotengine/godot
File: platform/linuxbsd/os_linuxbsd.cpp (C++)

Code before:
}

String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const {
	Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
	if (f.is_valid()) {
		while (!f->eof_reached()) {
			const String line = f->get_line();
			if (line.contains(key)) {
				String value = line.get_slice("=", 1).strip_edges();
				value = value.trim_prefix("\"");
				return value.trim_suffix("\"");
			}
		}
	}
	return "";
}

String OS_LinuxBSD::get_distribution_name() const {
	static String distribution_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
	if (!distribution_name.is_empty()) {
		return distribution_name;
	}
	struct utsname uts; // returns a decent value for BSD family.
	uname(&uts);
	distribution_name = uts.sysname;
	return distribution_name;
}

String OS_LinuxBSD::get_version() const {
	static String release_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string.
	if (!release_version.is_empty()) {
		return release_version;
	}
	struct utsname uts; // returns a decent value for BSD family.
	uname(&uts);
	release_version = uts.version;
	return release_version;
}

Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
	if (RenderingServer::get_singleton() == nullptr) {
		return Vector<String>();
	}

	static Vector<String> info;
	if (!info.is_empty()) {
		return info;
	}

	const String rendering_device_name = RenderingServer::get_singleton()->get_video_adapter_name(); // e.g. `NVIDIA GeForce GTX 970`
	const String rendering_device_vendor = RenderingServer::get_singleton()->get_video_adapter_vendor(); // e.g. `NVIDIA`

Review comment (suggestion):
```suggestion
				String value = line.get_slicec('=', 1).strip_edges();
```

Code after:
		memdelete(main_loop);
	}
	main_loop = nullptr;
}

void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
	main_loop = p_main_loop;
}

String OS_LinuxBSD::get_identifier() const {
	return "linuxbsd";
}

String OS_LinuxBSD::get_name() const {
#ifdef __linux__
	return "Linux";
#elif defined(__FreeBSD__)
	return "FreeBSD";
#elif defined(__NetBSD__)
	return "NetBSD";
#elif defined(__OpenBSD__)
	return "OpenBSD";
#else
	return "BSD";
#endif
}

String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const {
	Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
	if (f.is_valid()) {
		while (!f->eof_reached()) {
			const String line = f->get_line();
			if (line.contains(key)) {
				String value = line.get_slicec('=', 1).strip_edges();
				value = value.trim_prefix("\"");
				return value.trim_suffix("\"");
			}
		}
	}
	return "";
}

String OS_LinuxBSD::get_distribution_name() const {
	static String distribution_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
	if (!distribution_name.is_empty()) {
		return distribution_name;
	}
	struct utsname uts; // returns a decent value for BSD family.
	uname(&uts);
	distribution_name = uts.sysname;
	return distribution_name;
Repository: godotengine/godot
File: scene/animation/animation_blend_tree.cpp (C++)

Code before:
}

double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) {
	int current = get_parameter(this->current);
	int prev = get_parameter(this->prev);
	int prev_current = get_parameter(this->prev_current);

	double time = get_parameter(this->time);
	double prev_xfading = get_parameter(this->prev_xfading);

	bool switched = current != prev_current;

	if (switched) {
		set_parameter(this->prev_current, current);
		set_parameter(this->prev, prev_current);

		prev = prev_current;
		prev_xfading = xfade;
		time = 0;
		switched = true;
	}

	if (current < 0 || current >= enabled_inputs || prev >= enabled_inputs) {
		return 0;
	}

	double rem = 0.0;

	if (prev < 0) { // process current animation, check for transition

		rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);

		if (p_seek) {
			time = p_time;
		} else {
			time += p_time;
		}

		if (inputs[current].auto_advance && rem <= xfade) {
			set_parameter(this->current, (current + 1) % enabled_inputs);
		}

	} else { // cross-fading from prev to current

		float blend = xfade == 0 ? 0 : (prev_xfading / xfade);

		if (!p_seek && switched) { //just switched, seek to start of current

			rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
		} else {
			rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);

Review comment (suggestion):
Seek should be applied instead of stopping the animation, as internal Seek caused by OneShot or Transition can break sync. Even if it is not sync, it seems to me that there is no reason to stop it.

Code after:

		rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true);

		if (p_seek) {
			time = p_time;
		} else {
			time += p_time;
		}

		if (inputs[current].auto_advance && rem <= xfade) {
			set_parameter(this->current, (current + 1) % enabled_inputs);
		}

	} else { // cross-fading from prev to current

		float blend = xfade == 0 ? 0 : (prev_xfading / xfade);

		if (from_start && !p_seek && switched) { //just switched, seek to start of current

			rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, true);
		} else {
			rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, true);
		}

		if (p_seek) {
			blend_input(prev, p_time, true, p_seek_root, blend, FILTER_IGNORE, true);
			time = p_time;
		} else {
			blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, true);
			time += p_time;
			prev_xfading -= p_time;
			if (prev_xfading < 0) {
				set_parameter(this->prev, -1);
			}
		}
	}

	set_parameter(this->time, time);
	set_parameter(this->prev_xfading, prev_xfading);

	return rem;
}

void AnimationNodeTransition::_validate_property(PropertyInfo &property) const {
	if (property.name.begins_with("input_")) {
		String n = property.name.get_slicec('/', 0).get_slicec('_', 1);
		if (n != "count") {
			int idx = n.to_int();
			if (idx >= enabled_inputs) {
				property.usage = PROPERTY_USAGE_NONE;
			}
Repository: godotengine/godot
File: scene/gui/popup_menu.cpp (C++)

Code before:
void PopupMenu::_submenu_timeout() {
	if (mouse_over == submenu_over) {
		_activate_submenu(mouse_over);
	}

	submenu_over = -1;
}

void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
	ERR_FAIL_COND(p_event.is_null());

	if (!items.is_empty()) {
		if (p_event->is_action("ui_down", true) && p_event->is_pressed()) {
			int search_from = mouse_over + 1;
			if (search_from >= items.size()) {
				search_from = 0;
			}

			bool match_found = false;
			for (int i = search_from; i < items.size(); i++) {
				if (!items[i].separator && !items[i].disabled) {
					mouse_over = i;
					emit_signal(SNAME("id_focused"), i);
					scroll_to_item(i);
					control->queue_redraw();
					set_input_as_handled();
					match_found = true;
					break;
				}
			}

			if (!match_found) {
				// If the last item is not selectable, try re-searching from the start.
				for (int i = 0; i < search_from; i++) {
					if (!items[i].separator && !items[i].disabled) {
						mouse_over = i;
						emit_signal(SNAME("id_focused"), i);
						scroll_to_item(i);
						control->queue_redraw();
						set_input_as_handled();
						break;
					}
				}
			}
		} else if (p_event->is_action("ui_up", true) && p_event->is_pressed()) {
			int search_from = mouse_over - 1;
			if (search_from < 0) {
				search_from = items.size() - 1;
			}

			bool match_found = false;

Review comment (suggestion):
Ok sorry, this one was correct ;_;
I didn't notice that it's `gui_input` not `PROCESS`.
Same the one below.

Code after:
void PopupMenu::_submenu_timeout() {
	if (mouse_over == submenu_over) {
		_activate_submenu(mouse_over);
	}

	submenu_over = -1;
}

void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
	ERR_FAIL_COND(p_event.is_null());

	if (!items.is_empty()) {
		Input *input = Input::get_singleton();
		Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
		Ref<InputEventJoypadButton> joypadbutton_event = p_event;
		bool is_joypad_event = (joypadmotion_event.is_valid() || joypadbutton_event.is_valid());

		if (p_event->is_action("ui_down", true) && p_event->is_pressed()) {
			if (is_joypad_event) {
				if (!input->is_action_just_pressed("ui_down", true)) {
					return;
				}
				set_process_internal(true);
			}
			int search_from = mouse_over + 1;
			if (search_from >= items.size()) {
				search_from = 0;
			}

			bool match_found = false;
			for (int i = search_from; i < items.size(); i++) {
				if (!items[i].separator && !items[i].disabled) {
					mouse_over = i;
					emit_signal(SNAME("id_focused"), i);
					scroll_to_item(i);
					control->queue_redraw();
					set_input_as_handled();
					match_found = true;
					break;
				}
			}

			if (!match_found) {
				// If the last item is not selectable, try re-searching from the start.
				for (int i = 0; i < search_from; i++) {
					if (!items[i].separator && !items[i].disabled) {
						mouse_over = i;
						emit_signal(SNAME("id_focused"), i);
						scroll_to_item(i);
						control->queue_redraw();
						set_input_as_handled();
Repository: godotengine/godot
File: core/io/image.cpp (C++)

Code before:
	if (data.size() == 0) {
		return Ref<Image>();
	}

	ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>());

	Ref<Image> new_image;
	new_image.instantiate();
	new_image->create(width, height, false, Image::FORMAT_RGB8);

	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			new_image->set_pixel(col, row, get_pixel(col, row).linear_to_srgb());
		}
	}

	if (has_mipmaps()) {
		new_image->generate_mipmaps();
	}

	return new_image;
}

Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
	int ofs, size, w, h;
	get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h);

	Vector<uint8_t> new_data;
	new_data.resize(size);

	{
		uint8_t *wr = new_data.ptrw();
		const uint8_t *rd = data.ptr();
		memcpy(wr, rd + ofs, size);
	}

	Ref<Image> image;
	image.instantiate();
	image->width = w;
	image->height = h;
	image->format = format;
	image->data = new_data;

	image->mipmaps = false;
	return image;
}

void Image::bump_map_to_normal_map(float bump_scale) {
	ERR_FAIL_COND(!_can_modify(format));
	convert(Image::FORMAT_RF);


Review comment (suggestion):
Could use `create_empty()` to streamline the code.

Code after:
	convert(Image::FORMAT_RGBA8);

	{
		int len = data.size() / 4;
		uint8_t *data_ptr = data.ptrw();

		for (int i = 0; i < len; i++) {
			data_ptr[(i << 2) + 3] = data_ptr[(i << 2) + 0]; //x to w
			data_ptr[(i << 2) + 0] = data_ptr[(i << 2) + 1]; //y to xz
			data_ptr[(i << 2) + 2] = data_ptr[(i << 2) + 1];
		}
	}

	convert(Image::FORMAT_LA8);
}

Ref<Image> Image::rgbe_to_srgb() {
	if (data.size() == 0) {
		return Ref<Image>();
	}

	ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>());

	Ref<Image> new_image = create_empty(width, height, false, Image::FORMAT_RGB8);

	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			new_image->set_pixel(col, row, get_pixel(col, row).linear_to_srgb());
		}
	}

	if (has_mipmaps()) {
		new_image->generate_mipmaps();
	}

	return new_image;
}

Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
	int ofs, size, w, h;
	get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h);

	Vector<uint8_t> new_data;
	new_data.resize(size);

	{
		uint8_t *wr = new_data.ptrw();
		const uint8_t *rd = data.ptr();
		memcpy(wr, rd + ofs, size);
	}
Repository: godotengine/godot
File: modules/cvtt/image_compress_cvtt.cpp (C++)

Code before:
		h = MAX(h / 2, 1);
	}

	if (num_job_threads > 0) {
		Vector<Thread *> threads;
		threads.resize(num_job_threads);

		Thread **threads_wb = threads.ptrw();

		const CVTTCompressionRowTask *tasks_rb = tasks.ptr();

		job_queue.job_tasks = &tasks_rb[0];
		job_queue.current_task.set(0);
		job_queue.num_tasks = static_cast<uint32_t>(tasks.size());

		for (int i = 0; i < num_job_threads; i++) {
			threads_wb[i] = memnew(Thread);
			threads_wb[i]->start(_digest_job_queue, &job_queue);
		}
		_digest_job_queue(&job_queue);

		for (int i = 0; i < num_job_threads; i++) {
			threads_wb[i]->wait_to_finish();
			memdelete(threads_wb[i]);
		}
	}
	p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}

void image_decompress_cvtt(Image *p_image) {
	Image::Format target_format;
	bool is_signed = false;
	bool is_hdr = false;

	Image::Format input_format = p_image->get_format();

	switch (input_format) {
		case Image::FORMAT_BPTC_RGBA:
			target_format = Image::FORMAT_RGBA8;
			break;
		case Image::FORMAT_BPTC_RGBF:
		case Image::FORMAT_BPTC_RGBFU:
			target_format = Image::FORMAT_RGBH;
			is_signed = (input_format == Image::FORMAT_BPTC_RGBF);
			is_hdr = true;
			break;
		default:
			return; // Invalid input format
	};

	int w = p_image->get_width();

Review comment (suggestion):
Confirming that this code has been unused since 2018 so it's fine to remove. If we do want to do this with multiple job threads, it could be revisited by any interested contributor using WorkerThreadPool maybe.

Code after:
	switch (input_format) {
		case Image::FORMAT_BPTC_RGBA:
			target_format = Image::FORMAT_RGBA8;
			break;
		case Image::FORMAT_BPTC_RGBF:
		case Image::FORMAT_BPTC_RGBFU:
			target_format = Image::FORMAT_RGBH;
			is_signed = (input_format == Image::FORMAT_BPTC_RGBF);
			is_hdr = true;
			break;
		default:
			return; // Invalid input format
	};

	int w = p_image->get_width();
	int h = p_image->get_height();

	const uint8_t *rb = p_image->get_data().ptr();

	Vector<uint8_t> data;
	int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
	int mm_count = p_image->get_mipmap_count();
	data.resize(target_size);

	uint8_t *wb = data.ptrw();

	int bytes_per_pixel = is_hdr ? 6 : 4;

	int dst_ofs = 0;

	for (int i = 0; i <= mm_count; i++) {
		int src_ofs = p_image->get_mipmap_offset(i);

		const uint8_t *in_bytes = &rb[src_ofs];
		uint8_t *out_bytes = &wb[dst_ofs];

		cvtt::PixelBlockU8 output_blocks_ldr[cvtt::NumParallelBlocks];
		cvtt::PixelBlockF16 output_blocks_hdr[cvtt::NumParallelBlocks];

		for (int y_start = 0; y_start < h; y_start += 4) {
			int y_end = y_start + 4;

			for (int x_start = 0; x_start < w; x_start += 4 * cvtt::NumParallelBlocks) {
				int x_end = x_start + 4 * cvtt::NumParallelBlocks;

				uint8_t input_blocks[16 * cvtt::NumParallelBlocks];
				memset(input_blocks, 0, sizeof(input_blocks));

				unsigned int num_real_blocks = ((w - x_start) + 3) / 4;
				if (num_real_blocks > cvtt::NumParallelBlocks) {
					num_real_blocks = cvtt::NumParallelBlocks;
Repository: godotengine/godot
File: scene/resources/curve.cpp (C++)

Code before:
			end = idx;
		} else {
			start = idx;
		}
		idx = (end + start) / 2;
	}

	real_t offset_begin = baked_dist_cache[idx];
	real_t offset_end = baked_dist_cache[idx + 1];

	real_t idx_interval = offset_end - offset_begin;
	ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment.");

	real_t frac = (p_offset - offset_begin) / idx_interval;

	if (p_cubic) {
		Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
		Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
		return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
	} else {
		return r[idx].lerp(r[idx + 1], frac);
	}
}

PackedVector2Array Curve2D::get_baked_points() const {
	if (baked_cache_dirty) {
		_bake();
	}

	return baked_point_cache;
}

void Curve2D::set_bake_interval(real_t p_tolerance) {
	bake_interval = p_tolerance;
	mark_dirty();
}

real_t Curve2D::get_bake_interval() const {
	return bake_interval;
}

Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
	// Brute force method.

	if (baked_cache_dirty) {
		_bake();
	}

	// Validate: Curve may not have baked points.
	int pc = baked_point_cache.size();
	ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");

Review comment (suggestion):
```suggestion
	real_t path_length = get_baked_length(); // Ensure baked.
```

Code after:
			end = idx;
		} else {
			start = idx;
		}
		idx = (end + start) / 2;
	}

	real_t offset_begin = baked_dist_cache[idx];
	real_t offset_end = baked_dist_cache[idx + 1];

	real_t idx_interval = offset_end - offset_begin;
	ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment.");

	real_t frac = (p_offset - offset_begin) / idx_interval;

	if (p_cubic) {
		Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
		Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
		return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
	} else {
		return r[idx].lerp(r[idx + 1], frac);
	}
}

Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const {
	real_t path_length = get_baked_length(); // Ensure baked.
	ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0.");

	Vector2 pos = sample_baked(p_offset, p_cubic);

	real_t ahead = p_offset + p_lookahead;

	if (p_loop && ahead >= path_length) {
		// If our lookahead will loop, we need to check if the path is closed.
		int point_count = get_point_count();
		if (point_count > 0) {
			Vector2 start_point = get_point_position(0);
			Vector2 end_point = get_point_position(point_count - 1);
			if (start_point == end_point) {
				// Since the path is closed we want to 'smooth off'
				// the corner at the start/end.
				// So we wrap the lookahead back round.
				ahead = Math::fmod(ahead, path_length);
			}
		}
	}

	Vector2 ahead_pos = sample_baked(ahead, p_cubic);

	Vector2 tangent_to_curve;
	if (ahead_pos == pos) {
Repository: godotengine/godot
File: scene/resources/curve.cpp (C++)

Code before:
		} else {
			start = idx;
		}
		idx = (end + start) / 2;
	}

	real_t offset_begin = baked_dist_cache[idx];
	real_t offset_end = baked_dist_cache[idx + 1];

	real_t idx_interval = offset_end - offset_begin;
	ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment.");

	real_t frac = (p_offset - offset_begin) / idx_interval;

	if (p_cubic) {
		Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
		Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
		return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
	} else {
		return r[idx].lerp(r[idx + 1], frac);
	}
}

PackedVector2Array Curve2D::get_baked_points() const {
	if (baked_cache_dirty) {
		_bake();
	}

	return baked_point_cache;
}

void Curve2D::set_bake_interval(real_t p_tolerance) {
	bake_interval = p_tolerance;
	mark_dirty();
}

real_t Curve2D::get_bake_interval() const {
	return bake_interval;
}

Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
	// Brute force method.

	if (baked_cache_dirty) {
		_bake();
	}

	// Validate: Curve may not have baked points.
	int pc = baked_point_cache.size();
	ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");


Review comment (bug):
```suggestion
	ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0.");
```

Code after:
		} else {
			start = idx;
		}
		idx = (end + start) / 2;
	}

	real_t offset_begin = baked_dist_cache[idx];
	real_t offset_end = baked_dist_cache[idx + 1];

	real_t idx_interval = offset_end - offset_begin;
	ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment.");

	real_t frac = (p_offset - offset_begin) / idx_interval;

	if (p_cubic) {
		Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
		Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
		return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
	} else {
		return r[idx].lerp(r[idx + 1], frac);
	}
}

Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const {
	real_t path_length = get_baked_length(); // Ensure baked.
	ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0.");

	Vector2 pos = sample_baked(p_offset, p_cubic);

	real_t ahead = p_offset + p_lookahead;

	if (p_loop && ahead >= path_length) {
		// If our lookahead will loop, we need to check if the path is closed.
		int point_count = get_point_count();
		if (point_count > 0) {
			Vector2 start_point = get_point_position(0);
			Vector2 end_point = get_point_position(point_count - 1);
			if (start_point == end_point) {
				// Since the path is closed we want to 'smooth off'
				// the corner at the start/end.
				// So we wrap the lookahead back round.
				ahead = Math::fmod(ahead, path_length);
			}
		}
	}

	Vector2 ahead_pos = sample_baked(ahead, p_cubic);

	Vector2 tangent_to_curve;
	if (ahead_pos == pos) {
		// This will happen at the end of non-looping or non-closed paths.