| /* |
| * Copyright © 2023 Red Hat, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * Authors: Matthias Clasen <mclasen@redhat.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <math.h> |
| |
| #include "gskpathpointprivate.h" |
| #include "gskcontourprivate.h" |
| #include "gdk/gdkprivate.h" |
| |
| #define RAD_TO_DEG(x) ((x) / (G_PI / 180.f)) |
| |
| /** |
| * GskPathPoint: |
| * |
| * `GskPathPoint` is an opaque type representing a point on a path. |
| * |
| * It can be queried for properties of the path at that point, such as |
| * its tangent or its curvature. |
| * |
| * To obtain a `GskPathPoint`, use [method@Gsk.Path.get_closest_point], |
| * [method@Gsk.Path.get_start_point], [method@Gsk.Path.get_end_point] |
| * or [method@Gsk.PathMeasure.get_point]. |
| * |
| * Note that `GskPathPoint` structs are meant to be stack-allocated, |
| * and don't hold a reference to the path object they are obtained from. |
| * It is the callers responsibility to keep a reference to the path |
| * as long as the `GskPathPoint` is used. |
| * |
| * Since: 4.14 |
| */ |
| |
| G_DEFINE_BOXED_TYPE (GskPathPoint, gsk_path_point, |
| gsk_path_point_copy, |
| gsk_path_point_free) |
| |
| /** |
| * gsk_path_point_copy: |
| * @point: a path point |
| * |
| * Copies a path point. |
| * |
| * Returns: the copied point |
| * |
| * Since: 4.14 |
| */ |
| GskPathPoint * |
| gsk_path_point_copy (GskPathPoint *point) |
| { |
| GskPathPoint *copy; |
| |
| copy = g_new0 (GskPathPoint, 1); |
| |
| memcpy (copy, point, sizeof (GskPathPoint)); |
| |
| return copy; |
| } |
| |
| /** |
| * gsk_path_point_free: |
| * @point: a path point |
| * |
| * Frees a path point copied by [method@Gsk.PathPoint.copy]. |
| * |
| * Since: 4.14 |
| */ |
| void |
| gsk_path_point_free (GskPathPoint *point) |
| { |
| g_free (point); |
| } |
| |
| /** |
| * gsk_path_point_equal: |
| * @point1: a `GskPathPoint` |
| * @point2: another `GskPathPoint` |
| * |
| * Returns whether the two path points refer to the same |
| * location on all paths. |
| * |
| * Note that the start- and endpoint of a closed contour |
| * will compare nonequal according to this definition. |
| * Use [method@Gsk.Path.is_closed] to find out if the |
| * start- and endpoint of a concrete path refer to the |
| * same location. |
| * |
| * Return: `TRUE` if @point1 and @point2 are equal |
| * |
| * Since: 4.14 |
| */ |
| gboolean |
| gsk_path_point_equal (const GskPathPoint *point1, |
| const GskPathPoint *point2) |
| { |
| if (point1->contour == point2->contour) |
| { |
| if ((point1->idx == point2->idx && point1->t == point2->t) || |
| (point1->idx + 1 == point2->idx && point1->t == 1 && point2->t == 0) || |
| (point1->idx == point2->idx + 1 && point1->t == 0 && point2->t == 1)) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * gsk_path_point_compare: |
| * @point1: a `GskPathPoint` |
| * @point2: another `GskPathPoint` |
| * |
| * Returns whether @point1 is before or after @point2. |
| * |
| * Returns: -1 if @point1 is before @point2, |
| * 1 if @point1 is after @point2, |
| * 0 if they are equal |
| * |
| * Since: 4.14 |
| */ |
| int |
| gsk_path_point_compare (const GskPathPoint *point1, |
| const GskPathPoint *point2) |
| { |
| if (gsk_path_point_equal (point1, point2)) |
| return 0; |
| |
| if (point1->contour < point2->contour) |
| return -1; |
| else if (point1->contour > point2->contour) |
| return 1; |
| else if (point1->idx < point2->idx) |
| return -1; |
| else if (point1->idx > point2->idx) |
| return 1; |
| else if (point1->t < point2->t) |
| return -1; |
| else if (point1->t > point2->t) |
| return 1; |
| |
| return 0; |
| } |
| |
| /** |
| * gsk_path_point_get_position: |
| * @point: a `GskPathPoint` |
| * @path: the path that @point is on |
| * @position: (out caller-allocates): Return location for |
| * the coordinates of the point |
| * |
| * Gets the position of the point. |
| * |
| * Since: 4.14 |
| */ |
| void |
| gsk_path_point_get_position (const GskPathPoint *point, |
| GskPath *path, |
| graphene_point_t *position) |
| { |
| const GskContour *contour; |
| |
| g_return_if_fail (path != NULL); |
| g_return_if_fail (gsk_path_point_valid (point, path)); |
| g_return_if_fail (position != NULL); |
| |
| contour = gsk_path_get_contour (path, point->contour), |
| gsk_contour_get_position (contour, point, position); |
| } |
| |
| /** |
| * gsk_path_point_get_tangent: |
| * @point: a `GskPathPoint` |
| * @path: the path that @point is on |
| * @direction: the direction for which to return the tangent |
| * @tangent: (out caller-allocates): Return location for |
| * the tangent at the point |
| * |
| * Gets the tangent of the path at the point. |
| * |
| * Note that certain points on a path may not have a single |
| * tangent, such as sharp turns. At such points, there are |
| * two tangents -- the direction of the path going into the |
| * point, and the direction coming out of it. The @direction |
| * argument lets you choose which one to get. |
| * |
| * If the path is just a single point (e.g. a circle with |
| * radius zero), then @tangent is set to `0, 0`. |
| * |
| * If you want to orient something in the direction of the |
| * path, [method@Gsk.PathPoint.get_rotation] may be more |
| * convenient to use. |
| * |
| * Since: 4.14 |
| */ |
| void |
| gsk_path_point_get_tangent (const GskPathPoint *point, |
| GskPath *path, |
| GskPathDirection direction, |
| graphene_vec2_t *tangent) |
| { |
| const GskContour *contour; |
| |
| g_return_if_fail (path != NULL); |
| g_return_if_fail (gsk_path_point_valid (point, path)); |
| g_return_if_fail (tangent != NULL); |
| |
| contour = gsk_path_get_contour (path, point->contour), |
| gsk_contour_get_tangent (contour, point, direction, tangent); |
| } |
| |
| /** |
| * gsk_path_point_get_rotation: |
| * @point: a `GskPathPoint` |
| * @path: the path that @point is on |
| * @direction: the direction for which to return the rotation |
| * |
| * Gets the direction of the tangent at a given point. |
| * |
| * This is a convenience variant of [method@Gsk.PathPoint.get_tangent] |
| * that returns the angle between the tangent and the X axis. The angle |
| * can e.g. be used in |
| * [gtk_snapshot_rotate()](../gtk4/method.Snapshot.rotate.html). |
| * |
| * Returns: the angle between the tangent and the X axis, in degrees |
| * |
| * Since: 4.14 |
| */ |
| float |
| gsk_path_point_get_rotation (const GskPathPoint *point, |
| GskPath *path, |
| GskPathDirection direction) |
| { |
| graphene_vec2_t tangent; |
| |
| g_return_val_if_fail (path != NULL, 0); |
| g_return_val_if_fail (gsk_path_point_valid (point, path), 0); |
| |
| gsk_path_point_get_tangent (point, path, direction, &tangent); |
| |
| return RAD_TO_DEG (atan2f (graphene_vec2_get_y (&tangent), |
| graphene_vec2_get_x (&tangent))); |
| } |
| |
| /** |
| * gsk_path_point_get_curvature: |
| * @point: a `GskPathPoint` |
| * @path: the path that @point is on |
| * @direction: the direction for which to return the curvature |
| * @center: (out caller-allocates) (nullable): Return location for |
| * the center of the osculating circle |
| * |
| * Calculates the curvature of the path at the point. |
| * |
| * Optionally, returns the center of the osculating circle as well. |
| * The curvature is the inverse of the radius of the osculating circle. |
| * |
| * Lines have a curvature of zero (indicating an osculating circle of |
| * infinite radius. In this case, the @center is not modified. |
| * |
| * Circles with a radius of zero have `INFINITY` as curvature |
| * |
| * Note that certain points on a path may not have a single curvature, |
| * such as sharp turns. At such points, there are two curvatures -- |
| * the (limit of) the curvature of the path going into the point, |
| * and the (limit of) the curvature of the path coming out of it. |
| * The @direction argument lets you choose which one to get. |
| * |
| * <picture> |
| * <source srcset="curvature-dark.png" media="(prefers-color-scheme: dark)"> |
| * <img alt="Osculating circle" src="curvature-light.png"> |
| * </picture> |
| |
| * Returns: The curvature of the path at the given point |
| * |
| * Since: 4.14 |
| */ |
| float |
| gsk_path_point_get_curvature (const GskPathPoint *point, |
| GskPath *path, |
| GskPathDirection direction, |
| graphene_point_t *center) |
| { |
| const GskContour *contour; |
| |
| g_return_val_if_fail (path != NULL, 0); |
| g_return_val_if_fail (gsk_path_point_valid (point, path), 0); |
| |
| contour = gsk_path_get_contour (path, point->contour); |
| return gsk_contour_get_curvature (contour, point, direction, center); |
| } |