Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gimp-transform-utils.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <string.h>

#include <glib-object.h>

#include "libgimpmath/gimpmath.h"

#include "core-types.h"

#include "gimp-transform-utils.h"
#include "gimpcoords.h"
#include "gimpcoords-interpolate.h"


#define EPSILON 1e-6


void
gimp_transform_get_rotate_center (gint      x,
                                  gint      y,
                                  gint      width,
                                  gint      height,
                                  gboolean  auto_center,
                                  gdouble  *center_x,
                                  gdouble  *center_y)
{
  g_return_if_fail (center_x != NULL);
  g_return_if_fail (center_y != NULL);

  if (auto_center)
    {
      *center_x = (gdouble) x + (gdouble) width  / 2.0;
      *center_y = (gdouble) y + (gdouble) height / 2.0;
    }
}

void
gimp_transform_get_flip_axis (gint                 x,
                              gint                 y,
                              gint                 width,
                              gint                 height,
                              GimpOrientationType  flip_type,
                              gboolean             auto_center,
                              gdouble             *axis)
{
  g_return_if_fail (axis != NULL);

  if (auto_center)
    {
      switch (flip_type)
        {
        case GIMP_ORIENTATION_HORIZONTAL:
          *axis = ((gdouble) x + (gdouble) width / 2.0);
          break;

        case GIMP_ORIENTATION_VERTICAL:
          *axis = ((gdouble) y + (gdouble) height / 2.0);
          break;

        default:
          g_return_if_reached ();
          break;
        }
    }
}

void
gimp_transform_matrix_flip (GimpMatrix3         *matrix,
                            GimpOrientationType  flip_type,
                            gdouble              axis)
{
  g_return_if_fail (matrix != NULL);

  switch (flip_type)
    {
    case GIMP_ORIENTATION_HORIZONTAL:
      gimp_matrix3_translate (matrix, - axis, 0.0);
      gimp_matrix3_scale (matrix, -1.0, 1.0);
      gimp_matrix3_translate (matrix, axis, 0.0);
      break;

    case GIMP_ORIENTATION_VERTICAL:
      gimp_matrix3_translate (matrix, 0.0, - axis);
      gimp_matrix3_scale (matrix, 1.0, -1.0);
      gimp_matrix3_translate (matrix, 0.0, axis);
      break;

    case GIMP_ORIENTATION_UNKNOWN:
      break;
    }
}

void
gimp_transform_matrix_flip_free (GimpMatrix3 *matrix,
                                 gdouble      x1,
                                 gdouble      y1,
                                 gdouble      x2,
                                 gdouble      y2)
{
  gdouble angle;

  g_return_if_fail (matrix != NULL);

  angle = atan2  (y2 - y1, x2 - x1);

  gimp_matrix3_identity  (matrix);
  gimp_matrix3_translate (matrix, -x1, -y1);
  gimp_matrix3_rotate    (matrix, -angle);
  gimp_matrix3_scale     (matrix, 1.0, -1.0);
  gimp_matrix3_rotate    (matrix, angle);
  gimp_matrix3_translate (matrix, x1, y1);
}

void
gimp_transform_matrix_rotate (GimpMatrix3         *matrix,
                              GimpRotationType     rotate_type,
                              gdouble              center_x,
                              gdouble              center_y)
{
  gdouble angle = 0;

  switch (rotate_type)
    {
    case GIMP_ROTATE_DEGREES90:
      angle = G_PI_2;
      break;
    case GIMP_ROTATE_DEGREES180:
      angle = G_PI;
      break;
    case GIMP_ROTATE_DEGREES270:
      angle = - G_PI_2;
      break;
    }

  gimp_transform_matrix_rotate_center (matrix, center_x, center_y, angle);
}

void
gimp_transform_matrix_rotate_rect (GimpMatrix3 *matrix,
                                   gint         x,
                                   gint         y,
                                   gint         width,
                                   gint         height,
                                   gdouble      angle)
{
  gdouble center_x;
  gdouble center_y;

  g_return_if_fail (matrix != NULL);

  center_x = (gdouble) x + (gdouble) width  / 2.0;
  center_y = (gdouble) y + (gdouble) height / 2.0;

  gimp_matrix3_translate (matrix, -center_x, -center_y);
  gimp_matrix3_rotate    (matrix, angle);
  gimp_matrix3_translate (matrix, +center_x, +center_y);
}

void
gimp_transform_matrix_rotate_center (GimpMatrix3 *matrix,
                                     gdouble      center_x,
                                     gdouble      center_y,
                                     gdouble      angle)
{
  g_return_if_fail (matrix != NULL);

  gimp_matrix3_translate (matrix, -center_x, -center_y);
  gimp_matrix3_rotate    (matrix, angle);
  gimp_matrix3_translate (matrix, +center_x, +center_y);
}

void
gimp_transform_matrix_scale (GimpMatrix3 *matrix,
                             gint         x,
                             gint         y,
                             gint         width,
                             gint         height,
                             gdouble      t_x,
                             gdouble      t_y,
                             gdouble      t_width,
                             gdouble      t_height)
{
  gdouble scale_x = 1.0;
  gdouble scale_y = 1.0;

  g_return_if_fail (matrix != NULL);

  if (width > 0)
    scale_x = t_width / (gdouble) width;

  if (height > 0)
    scale_y = t_height / (gdouble) height;

  gimp_matrix3_identity  (matrix);
  gimp_matrix3_translate (matrix, -x, -y);
  gimp_matrix3_scale     (matrix, scale_x, scale_y);
  gimp_matrix3_translate (matrix, t_x, t_y);
}

void
gimp_transform_matrix_shear (GimpMatrix3         *matrix,
                             gint                 x,
                             gint                 y,
                             gint                 width,
                             gint                 height,
                             GimpOrientationType  orientation,
                             gdouble              amount)
{
  gdouble center_x;
  gdouble center_y;

  g_return_if_fail (matrix != NULL);

  if (width == 0)
    width = 1;

  if (height == 0)
    height = 1;

  center_x = (gdouble) x + (gdouble) width  / 2.0;
  center_y = (gdouble) y + (gdouble) height / 2.0;

  gimp_matrix3_identity  (matrix);
  gimp_matrix3_translate (matrix, -center_x, -center_y);

  if (orientation == GIMP_ORIENTATION_HORIZONTAL)
    gimp_matrix3_xshear (matrix, amount / height);
  else
    gimp_matrix3_yshear (matrix, amount / width);

  gimp_matrix3_translate (matrix, +center_x, +center_y);
}

void
gimp_transform_matrix_perspective (GimpMatrix3 *matrix,
                                   gint         x,
                                   gint         y,
                                   gint         width,
                                   gint         height,
                                   gdouble      t_x1,
                                   gdouble      t_y1,
                                   gdouble      t_x2,
                                   gdouble      t_y2,
                                   gdouble      t_x3,
                                   gdouble      t_y3,
                                   gdouble      t_x4,
                                   gdouble      t_y4)
{
  GimpMatrix3 trafo;
  gdouble     scalex;
  gdouble     scaley;

  g_return_if_fail (matrix != NULL);

  scalex = scaley = 1.0;

  if (width > 0)
    scalex = 1.0 / (gdouble) width;

  if (height > 0)
    scaley = 1.0 / (gdouble) height;

  gimp_matrix3_translate (matrix, -x, -y);
  gimp_matrix3_scale     (matrix, scalex, scaley);

  /* Determine the perspective transform that maps from
   * the unit cube to the transformed coordinates
   */

  {
    gdouble dx1, dx2, dx3, dy1, dy2, dy3;

    dx1 = t_x2 - t_x4;
    dx2 = t_x3 - t_x4;
    dx3 = t_x1 - t_x2 + t_x4 - t_x3;

    dy1 = t_y2 - t_y4;
    dy2 = t_y3 - t_y4;
    dy3 = t_y1 - t_y2 + t_y4 - t_y3;

    /*  Is the mapping affine?  */
    if ((dx3 == 0.0) && (dy3 == 0.0))
      {
        trafo.coeff[0][0] = t_x2 - t_x1;
        trafo.coeff[0][1] = t_x4 - t_x2;
        trafo.coeff[0][2] = t_x1;
        trafo.coeff[1][0] = t_y2 - t_y1;
        trafo.coeff[1][1] = t_y4 - t_y2;
        trafo.coeff[1][2] = t_y1;
        trafo.coeff[2][0] = 0.0;
        trafo.coeff[2][1] = 0.0;
      }
    else
      {
        gdouble det1, det2;

        det1 = dx3 * dy2 - dy3 * dx2;
        det2 = dx1 * dy2 - dy1 * dx2;

        trafo.coeff[2][0] = (det2 == 0.0) ? 1.0 : det1 / det2;

        det1 = dx1 * dy3 - dy1 * dx3;

        trafo.coeff[2][1] = (det2 == 0.0) ? 1.0 : det1 / det2;

        trafo.coeff[0][0] = t_x2 - t_x1 + trafo.coeff[2][0] * t_x2;
        trafo.coeff[0][1] = t_x3 - t_x1 + trafo.coeff[2][1] * t_x3;
        trafo.coeff[0][2] = t_x1;

        trafo.coeff[1][0] = t_y2 - t_y1 + trafo.coeff[2][0] * t_y2;
        trafo.coeff[1][1] = t_y3 - t_y1 + trafo.coeff[2][1] * t_y3;
        trafo.coeff[1][2] = t_y1;
      }

    trafo.coeff[2][2] = 1.0;
  }

  gimp_matrix3_mult (&trafo, matrix);
}

/* modified gaussian algorithm
 * solves a system of linear equations
 *
 * Example:
 * 1x + 2y + 4z = 25
 * 2x + 1y      = 4
 * 3x + 5y + 2z = 23
 * Solution: x=1, y=2, z=5
 *
 * Input:
 * matrix = { 1,2,4,25,2,1,0,4,3,5,2,23 }
 * s = 3 (Number of variables)
 * Output:
 * return value == TRUE (TRUE, if there is a single unique solution)
 * solution == { 1,2,5 } (if the return value is FALSE, the content
 * of solution is of no use)
 */

static gboolean
mod_gauss (gdouble matrix[],
           gdouble solution[],
           gint    s)
{
  gint    p[s]; /* row permutation */
  gint    i, j, r, temp;
  gdouble q;
  gint    t = s + 1;

  for (i = 0; i < s; i++)
    {
      p[i] = i;
    }

  for (r = 0; r < s; r++)
    {
      /* make sure that (r,r) is not 0 */
      if (fabs (matrix[p[r] * t + r]) <= EPSILON)
        {
          /* we need to permutate rows */
          for (i = r + 1; i <= s; i++)
            {
              if (i == s)
                {
                  /* if this happens, the linear system has zero or
                   * more than one solutions.
                   */

                  return FALSE;
                }

              if (fabs (matrix[p[i] * t + r]) > EPSILON)
                break;
            }

          temp = p[r];
          p[r] = p[i];
          p[i] = temp;
        }

      /* make (r,r) == 1 */
      q = 1.0 / matrix[p[r] * t + r];
      matrix[p[r] * t + r] = 1.0;

      for (j = r + 1; j < t; j++)
        {
          matrix[p[r] * t + j] *= q;
        }

      /* make that all entries in column r are 0 (except (r,r)) */
      for (i = 0; i < s; i++)
        {
          if (i == r)
            continue;

          for (j = r + 1; j < t ; j++)
            {
              matrix[p[i] * t + j] -= matrix[p[r] * t + j] * matrix[p[i] * t + r];
            }

          /* we don't need to execute the following line
           * since we won't access this element again:
           *
           * matrix[p[i] * t + r] = 0.0;
           */

        }
    }

  for (i = 0; i < s; i++)
    {
      solution[i] = matrix[p[i] * t + s];
    }

  return TRUE;
}

/* multiplies 'matrix' by the matrix that transforms a set of 4 'input_points'
 * to corresponding 'output_points', if such matrix exists, and is valid (i.e.,
 * keeps the output points in front of the camera).
 *
 * returns TRUE if successful.
 */

gboolean
gimp_transform_matrix_generic (GimpMatrix3       *matrix,
                               const GimpVector2  input_points[4],
                               const GimpVector2  output_points[4])
{
  GimpMatrix3 trafo;
  gdouble     coeff[8 * 9];
  gboolean    negative = -1;
  gint        i;
  gboolean    result   = TRUE;

  g_return_val_if_fail (matrix != NULL, FALSE);
  g_return_val_if_fail (input_points != NULL, FALSE);
  g_return_val_if_fail (output_points != NULL, FALSE);

  /* find the matrix that transforms 'input_points' to 'output_points', whose
   * (3, 3) coefficient is 1, by solving a system of linear equations whose
   * solution is the remaining 8 coefficients.
   */

  for (i = 0; i < 4; i++)
    {
      coeff[i * 9 + 0] = input_points[i].x;
      coeff[i * 9 + 1] = input_points[i].y;
      coeff[i * 9 + 2] = 1.0;
      coeff[i * 9 + 3] = 0.0;
      coeff[i * 9 + 4] = 0.0;
      coeff[i * 9 + 5] = 0.0;
      coeff[i * 9 + 6] = -input_points[i].x * output_points[i].x;
      coeff[i * 9 + 7] = -input_points[i].y * output_points[i].x;
      coeff[i * 9 + 8] =                      output_points[i].x;

      coeff[(i + 4) * 9 + 0] = 0.0;
      coeff[(i + 4) * 9 + 1] = 0.0;
      coeff[(i + 4) * 9 + 2] = 0.0;
      coeff[(i + 4) * 9 + 3] = input_points[i].x;
      coeff[(i + 4) * 9 + 4] = input_points[i].y;
      coeff[(i + 4) * 9 + 5] = 1.0;
      coeff[(i + 4) * 9 + 6] = -input_points[i].x * output_points[i].y;
      coeff[(i + 4) * 9 + 7] = -input_points[i].y * output_points[i].y;
      coeff[(i + 4) * 9 + 8] =                      output_points[i].y;
    }

  /* if there is no solution, bail */
  if (! mod_gauss (coeff, (gdouble *) trafo.coeff, 8))
    return FALSE;

  trafo.coeff[2][2] = 1.0;

  /* make sure that none of the input points maps to a point at infinity, and
   * that all output points are on the same side of the camera.
   */

  for (i = 0; i < 4; i++)
    {
      gdouble  w;
      gboolean neg;

      w = trafo.coeff[2][0] * input_points[i].x +
          trafo.coeff[2][1] * input_points[i].y +
          trafo.coeff[2][2];

      if (fabs (w) <= EPSILON)
        result = FALSE;

      neg = (w < 0.0);

      if (negative < 0)
        {
          negative = neg;
        }
      else if (neg != negative)
        {
          result = FALSE;
          break;
        }
    }

  /* if the output points are all behind the camera, negate the matrix, which
   * would map the input points to the corresponding points in front of the
   * camera.
   */

  if (negative > 0)
    {
      gint r;
      gint c;

      for (r = 0; r < 3; r++)
        {
          for (c = 0; c < 3; c++)
            {
              trafo.coeff[r][c] = -trafo.coeff[r][c];
            }
        }
    }

  /* append the transformation to 'matrix' */
  gimp_matrix3_mult (&trafo, matrix);

  return result;
}

gboolean
gimp_transform_polygon_is_convex (gdouble x1,
                                  gdouble y1,
                                  gdouble x2,
                                  gdouble y2,
                                  gdouble x3,
                                  gdouble y3,
                                  gdouble x4,
                                  gdouble y4)
{
  gdouble z1, z2, z3, z4;

  /* We test if the transformed polygon is convex.  if z1 and z2 have
   * the same sign as well as z3 and z4 the polygon is convex.
   */

  z1 = ((x2 - x1) * (y4 - y1) -
        (x4 - x1) * (y2 - y1));
  z2 = ((x4 - x1) * (y3 - y1) -
        (x3 - x1) * (y4 - y1));
  z3 = ((x4 - x2) * (y3 - y2) -
        (x3 - x2) * (y4 - y2));
  z4 = ((x3 - x2) * (y1 - y2) -
        (x1 - x2) * (y3 - y2));

  return (z1 * z2 > 0) && (z3 * z4 > 0);
}

/* transforms the polygon or polyline, whose vertices are given by 'vertices',
 * by 'matrix', performing clipping by the near plane.  'closed' indicates
 * whether the vertices represent a polygon ('closed == TRUE') or a polyline
 * ('closed == FALSE').
 *
 * returns the transformed vertices in 't_vertices', and their count in
 * 'n_t_vertices'.  the minimal possible number of transformed vertices is 0,
 * which happens when the entire input is clipped.  in general, the maximal
 * possible number of transformed vertices is '3 * n_vertices / 2' (rounded
 * down), however, for convex polygons the number is 'n_vertices + 1', and for
 * a single line segment ('n_vertices == 2' and 'closed == FALSE') the number
 * is 2.
 *
 * 't_vertices' may not alias 'vertices', except when transforming a single
 * line segment.
 */

void
gimp_transform_polygon (const GimpMatrix3 *matrix,
                        const GimpVector2 *vertices,
                        gint               n_vertices,
                        gboolean           closed,
                        GimpVector2       *t_vertices,
                        gint              *n_t_vertices)
{
  GimpVector3 curr;
  gboolean    curr_visible;
  gint        i;

  g_return_if_fail (matrix != NULL);
  g_return_if_fail (vertices != NULL);
  g_return_if_fail (n_vertices >= 0);
  g_return_if_fail (t_vertices != NULL);
  g_return_if_fail (n_t_vertices != NULL);

  *n_t_vertices = 0;

  if (n_vertices == 0)
    return;

  curr.x = matrix->coeff[0][0] * vertices[0].x +
           matrix->coeff[0][1] * vertices[0].y +
           matrix->coeff[0][2];
  curr.y = matrix->coeff[1][0] * vertices[0].x +
           matrix->coeff[1][1] * vertices[0].y +
           matrix->coeff[1][2];
  curr.z = matrix->coeff[2][0] * vertices[0].x +
           matrix->coeff[2][1] * vertices[0].y +
           matrix->coeff[2][2];

  curr_visible = (curr.z >= GIMP_TRANSFORM_NEAR_Z);

  for (i = 0; i < n_vertices; i++)
    {
      if (curr_visible)
        {
          t_vertices[(*n_t_vertices)++] = (GimpVector2) { curr.x / curr.z,
                                                          curr.y / curr.z };
        }

      if (i < n_vertices - 1 || closed)
        {
          GimpVector3 next;
          gboolean    next_visible;
          gint        j = (i + 1) % n_vertices;

          next.x = matrix->coeff[0][0] * vertices[j].x +
                   matrix->coeff[0][1] * vertices[j].y +
                   matrix->coeff[0][2];
          next.y = matrix->coeff[1][0] * vertices[j].x +
                   matrix->coeff[1][1] * vertices[j].y +
                   matrix->coeff[1][2];
          next.z = matrix->coeff[2][0] * vertices[j].x +
                   matrix->coeff[2][1] * vertices[j].y +
                   matrix->coeff[2][2];

          next_visible = (next.z >= GIMP_TRANSFORM_NEAR_Z);

          if (next_visible != curr_visible)
            {
              gdouble ratio = (curr.z - GIMP_TRANSFORM_NEAR_Z) / (curr.z - next.z);

              t_vertices[(*n_t_vertices)++] =
                (GimpVector2) { (curr.x + (next.x - curr.x) * ratio) / GIMP_TRANSFORM_NEAR_Z,
                                (curr.y + (next.y - curr.y) * ratio) / GIMP_TRANSFORM_NEAR_Z };
            }

          curr         = next;
          curr_visible = next_visible;
        }
    }
}

/* same as gimp_transform_polygon(), but using GimpCoords as the vertex type,
 * instead of GimpVector2.
 */

void
gimp_transform_polygon_coords (const GimpMatrix3 *matrix,
                               const GimpCoords  *vertices,
                               gint               n_vertices,
                               gboolean           closed,
                               GimpCoords        *t_vertices,
                               gint              *n_t_vertices)
{
  GimpVector3 curr;
  gboolean    curr_visible;
  gint        i;

  g_return_if_fail (matrix != NULL);
  g_return_if_fail (vertices != NULL);
  g_return_if_fail (n_vertices >= 0);
  g_return_if_fail (t_vertices != NULL);
  g_return_if_fail (n_t_vertices != NULL);

  *n_t_vertices = 0;

  if (n_vertices == 0)
    return;

  curr.x = matrix->coeff[0][0] * vertices[0].x +
           matrix->coeff[0][1] * vertices[0].y +
           matrix->coeff[0][2];
  curr.y = matrix->coeff[1][0] * vertices[0].x +
           matrix->coeff[1][1] * vertices[0].y +
           matrix->coeff[1][2];
  curr.z = matrix->coeff[2][0] * vertices[0].x +
           matrix->coeff[2][1] * vertices[0].y +
           matrix->coeff[2][2];

  curr_visible = (curr.z >= GIMP_TRANSFORM_NEAR_Z);

  for (i = 0; i < n_vertices; i++)
    {
      if (curr_visible)
        {
          t_vertices[*n_t_vertices]   = vertices[i];
          t_vertices[*n_t_vertices].x = curr.x / curr.z;
          t_vertices[*n_t_vertices].y = curr.y / curr.z;

          (*n_t_vertices)++;
        }

      if (i < n_vertices - 1 || closed)
        {
          GimpVector3 next;
          gboolean    next_visible;
          gint        j = (i + 1) % n_vertices;

          next.x = matrix->coeff[0][0] * vertices[j].x +
                   matrix->coeff[0][1] * vertices[j].y +
                   matrix->coeff[0][2];
          next.y = matrix->coeff[1][0] * vertices[j].x +
                   matrix->coeff[1][1] * vertices[j].y +
                   matrix->coeff[1][2];
          next.z = matrix->coeff[2][0] * vertices[j].x +
                   matrix->coeff[2][1] * vertices[j].y +
                   matrix->coeff[2][2];

          next_visible = (next.z >= GIMP_TRANSFORM_NEAR_Z);

          if (next_visible != curr_visible)
            {
              gdouble ratio = (curr.z - GIMP_TRANSFORM_NEAR_Z) / (curr.z - next.z);

              gimp_coords_mix (1.0 - ratio, &vertices[i],
                                     ratio, &vertices[j],
                                            &t_vertices[*n_t_vertices]);

              t_vertices[*n_t_vertices].x = (curr.x + (next.x - curr.x) * ratio) /
                                             GIMP_TRANSFORM_NEAR_Z;
              t_vertices[*n_t_vertices].y = (curr.y + (next.y - curr.y) * ratio) /
                                             GIMP_TRANSFORM_NEAR_Z;

              (*n_t_vertices)++;
            }

          curr         = next;
          curr_visible = next_visible;
        }
    }
}

/* returns the value of the polynomial 'poly', of degree 'degree', at 'x'.  the
 * coefficients of 'poly' should be specified in descending-degree order.
 */

static gdouble
polynomial_eval (const gdouble *poly,
                 gint           degree,
                 gdouble        x)
{
  gdouble y = poly[0];
  gint    i;

  for (i = 1; i <= degree; i++)
    y = y * x + poly[i];

  return y;
}

/* derives the polynomial 'poly', of degree 'degree'.
 *
 * returns the derivative in 'result'.
 */

static void
polynomial_derive (const gdouble *poly,
                   gint           degree,
                   gdouble       *result)
{
  while (degree)
    *result++ = *poly++ * degree--;
}

/* finds the real odd-multiplicity root of the polynomial 'poly', of degree
 * 'degree', inside the range '(x1, x2)'.
 *
 * returns TRUE if such a root exists, and stores its value in '*root'.
 *
 * 'poly' shall be monotonic in the range '(x1, x2)'.
 */

static gboolean
polynomial_odd_root (const gdouble *poly,
                     gint           degree,
                     gdouble        x1,
                     gdouble        x2,
                     gdouble       *root)
{
  gdouble y1;
  gdouble y2;
  gint    i;

  y1 = polynomial_eval (poly, degree, x1);
  y2 = polynomial_eval (poly, degree, x2);

  if (y1 * y2 > -EPSILON)
    {
      /* the two endpoints have the same sign, or one of them is zero.  there's
       * no root inside the range.
       */

      return FALSE;
    }
  else if (y1 > 0.0)
    {
      gdouble t;

      /* if the first endpoint is positive, swap the endpoints, so that the
       * first endpoint is always negative, and the second endpoint is always
       * positive.
       */


      t  = x1;
      x1 = x2;
      x2 = t;
    }

  /* approximate the root using binary search */
  for (i = 0; i < 53; i++)
    {
      gdouble x = (x1 + x2) / 2.0;
      gdouble y = polynomial_eval (poly, degree, x);

      if (y > 0.0)
        x2 = x;
      else
        x1 = x;
    }

  *root = (x1 + x2) / 2.0;

  return TRUE;
}

/* finds the real odd-multiplicity roots of the polynomial 'poly', of degree
 * 'degree', inside the range '(x1, x2)'.
 *
 * returns the roots in 'roots', in ascending order, and their count in
 * 'n_roots'.
 */

static void
polynomial_odd_roots (const gdouble *poly,
                      gint           degree,
                      gdouble        x1,
                      gdouble        x2,
                      gdouble       *roots,
                      gint          *n_roots)
{
  *n_roots = 0;

  /* find the real degree of the polynomial (skip any leading coefficients that
   * are 0)
   */

  for (; degree && fabs (*poly) < EPSILON; poly++, degree--);

  #define ADD_ROOT(root) \
    do \
      { \
        gdouble r = (root); \
        \
        if (r > x1 && r < x2) \
          roots[(*n_roots)++] = r; \
      } \
    while (FALSE)

  switch (degree)
    {
    /* constant case */
    case 0:
      break;

    /* linear case */
    case 1:
      ADD_ROOT (-poly[1] / poly[0]);
      break;

    /* quadratic case */
    case 2:
      {
        gdouble s = SQR (poly[1]) - 4 * poly[0] * poly[2];

        if (s > EPSILON)
          {
            s = sqrt (s);

            if (poly[0] < 0.0)
              s = -s;

            ADD_ROOT ((-poly[1] - s) / (2.0 * poly[0]));
            ADD_ROOT ((-poly[1] + s) / (2.0 * poly[0]));
          }

        break;
      }

    /* general case */
    default:
      {
        gdouble deriv[degree];
        gdouble deriv_roots[degree - 1];
        gint    n_deriv_roots;
        gdouble a;
        gdouble b;
        gint    i;

        /* find the odd roots of the derivative, i.e., the local extrema of the
         * polynomial
         */

        polynomial_derive (poly, degree, deriv);
        polynomial_odd_roots (deriv, degree - 1, x1, x2,
                              deriv_roots, &n_deriv_roots);

        /* search for roots between each consecutive pair of extrema, including
         * the endpoints
         */

        a = x1;

        for (i = 0; i <= n_deriv_roots; i++)
          {
            if (i < n_deriv_roots)
              b = deriv_roots[i];
            else
              b = x2;

            *n_roots += polynomial_odd_root (poly, degree, a, b,
                                             &roots[*n_roots]);

            a = b;
          }

        break;
      }
    }

  #undef ADD_ROOT
}

/* clips the cubic bezier segment, defined by the four control points 'bezier',
 * to the halfplane 'ax + by + c >= 0'.
 *
 * returns the clipped set of bezier segments in 'c_bezier', and their count in
 * 'n_c_bezier'.  the minimal possible number of clipped segments is 0, which
 * happens when the entire segment is clipped.  the maximal possible number of
 * clipped segments is 2.
 *
 * if the first clipped segment is an initial segment of 'bezier', sets
 * '*start_in' to TRUE, otherwise to FALSE.  if the last clipped segment is a
 * final segment of 'bezier', sets '*end_in' to TRUE, otherwise to FALSE.
 *
 * 'c_bezier' may not alias 'bezier'.
 */

static void
clip_bezier (const GimpCoords  bezier[4],
             gdouble           a,
             gdouble           b,
             gdouble           c,
             GimpCoords        c_bezier[2][4],
             gint             *n_c_bezier,
             gboolean         *start_in,
             gboolean         *end_in)
{
  gdouble dot[4];
  gdouble poly[4];
  gdouble roots[5];
  gint    n_roots;
  gint    n_positive;
  gint    i;

  n_positive = 0;

  for (i = 0; i < 4; i++)
    {
      dot[i] = a * bezier[i].x + b * bezier[i].y + c;

      n_positive += (dot[i] >= 0.0);
    }

  if (n_positive == 0)
    {
      /* all points are out -- the entire segment is out */

      *n_c_bezier = 0;
      *start_in   = FALSE;
      *end_in     = FALSE;

      return;
    }
  else if (n_positive == 4)
    {
      /* all points are in -- the entire segment is in */

      memcpy (c_bezier[0], bezier, sizeof (GimpCoords[4]));

      *n_c_bezier = 1;
      *start_in   = TRUE;
      *end_in     = TRUE;

      return;
    }

  /* find the points of intersection of the segment with the 'ax + by + c = 0'
   * line
   */

  poly[0] = dot[3] - 3.0 * dot[2] + 3.0 * dot[1] - dot[0];
  poly[1] = 3.0 * (dot[2] - 2.0 * dot[1] + dot[0]);
  poly[2] = 3.0 * (dot[1] - dot[0]);
  poly[3] = dot[0];

  roots[0] = 0.0;
  polynomial_odd_roots (poly, 3, 0.0, 1.0, roots + 1, &n_roots);
  roots[++n_roots] = 1.0;

  /* construct the list of segments that are inside the halfplane */
  *n_c_bezier = 0;
  *start_in   = (polynomial_eval (poly, 3, roots[1] / 2.0) > 0.0);
  *end_in     = (*start_in + n_roots + 1) % 2;

  for (i = ! *start_in; i < n_roots; i += 2)
    {
      gdouble t0 = roots[i];
      gdouble t1 = roots[i + 1];

      gimp_coords_interpolate_bezier_at (bezier, t0,
                                         &c_bezier[*n_c_bezier][0],
                                         &c_bezier[*n_c_bezier][1]);
      gimp_coords_interpolate_bezier_at (bezier, t1,
                                         &c_bezier[*n_c_bezier][3],
                                         &c_bezier[*n_c_bezier][2]);

      gimp_coords_mix (1.0,             &c_bezier[*n_c_bezier][0],
                       (t1 - t0) / 3.0, &c_bezier[*n_c_bezier][1],
                       &c_bezier[*n_c_bezier][1]);
      gimp_coords_mix (1.0,             &c_bezier[*n_c_bezier][3],
                       (t0 - t1) / 3.0, &c_bezier[*n_c_bezier][2],
                       &c_bezier[*n_c_bezier][2]);

      (*n_c_bezier)++;
    }
}

/* transforms the cubic bezier segment, defined by the four control points
 * 'bezier', by 'matrix', subdividing it as necessary to avoid diverging too
 * much from the real transformed curve.  at most 'depth' subdivisions are
 * performed.
 *
 * appends the transformed sequence of bezier segments to 't_beziers'.
 *
 * 'bezier' shall be fully clipped to the near plane.
 */

static void
transform_bezier_coords (const GimpMatrix3 *matrix,
                         const GimpCoords   bezier[4],
                         GQueue            *t_beziers,
                         gint               depth)
{
  GimpCoords *t_bezier;
  gint        n;

  /* check if we need to split the segment */
  if (depth > 0)
    {
      GimpVector2 v[4];
      GimpVector2 c[2];
      GimpVector2 b;
      gint        i;

      for (i = 0; i < 4; i++)
        v[i] = (GimpVector2) { bezier[i].x, bezier[i].y };

      gimp_vector2_sub (&c[0], &v[1], &v[style='color: green'>0]);
      gimp_vector2_sub (&c[1], &v[2], &v[style='color: green'>3]);

      gimp_vector2_sub (&b, &v[3], &v[0]);
      gimp_vector2_mul (&b, 1.0 / gimp_vector2_inner_product (&b, &b));

      for (i = 0; i < 2; i++)
        {
          /* split the segment if one of the control points is too far from the
           * line connecting the anchors
           */

          if (fabs (gimp_vector2_cross_product (&c[i], &b).x) > 0.5)
            {
              GimpCoords mid_position;
              GimpCoords mid_velocity;
              GimpCoords sub[4];

              gimp_coords_interpolate_bezier_at (bezier, 0.5,
                                                 &mid_position, &mid_velocity);

              /* first half */
              sub[0] = bezier[0];
              sub[3] = mid_position;

              gimp_coords_mix (0.5,        &sub[0],
                               0.5,        &bezier[1],
                                           &sub[1]);
              gimp_coords_mix (1.0,        &sub[3],
                               -1.0 / 6.0, &mid_velocity,
                                           &sub[2]);

              transform_bezier_coords (matrix, sub, t_beziers, depth - 1);

              /* second half */
              sub[0] = mid_position;
              sub[3] = bezier[3];

              gimp_coords_mix (1.0,        &sub[0],
                               +1.0 / 6.0, &mid_velocity,
                                           &sub[1]);
              gimp_coords_mix (0.5,        &sub[3],
                               0.5,        &bezier[2],
                                           &sub[2]);

              transform_bezier_coords (matrix, sub, t_beziers, depth - 1);

              return;
            }
        }
    }

  /* transform the segment by transforming each of the individual points.  note
   * that, for non-affine transforms, this is only an approximation of the real
   * transformed curve, but due to subdivision it should be good enough.
   */

  t_bezier = g_new (GimpCoords, 4);

  /* note that while the segments themselves are clipped to the near plane,
   * their control points may still get transformed behind the camera.  we
   * therefore clip the control points to the near plane as well, which is not
   * too meaningful, but avoids erroneously transforming them behind the
   * camera.
   */

  gimp_transform_polygon_coords (matrix, bezier, 2, FALSE,
                                 t_bezier, &n);
  gimp_transform_polygon_coords (matrix, bezier + 2, 2, FALSE,
                                 t_bezier + 2, &n);

  g_queue_push_tail (t_beziers, t_bezier);
}

/* transforms the cubic bezier segment, defined by the four control points
 * 'bezier', by 'matrix', performing clipping by the near plane and subdividing
 * as necessary.
 *
 * returns the transformed set of bezier-segment sequences in 't_beziers', as
 * GQueues of GimpCoords[4] bezier-segments, and the number of sequences in
 * 'n_t_beziers'.  the minimal possible number of transformed sequences is 0,
 * which happens when the entire segment is clipped.  the maximal possible
 * number of transformed sequences is 2.  each sequence has at least one
 * segment.
 *
 * if the first transformed segment is an initial segment of 'bezier', sets
 * '*start_in' to TRUE, otherwise to FALSE.  if the last transformed segment is
 * a final segment of 'bezier', sets '*end_in' to TRUE, otherwise to FALSE.
 */

void
gimp_transform_bezier_coords (const GimpMatrix3 *matrix,
                              const GimpCoords   bezier[4],
                              GQueue            *t_beziers[2],
                              gint              *n_t_beziers,
                              gboolean          *start_in,
                              gboolean          *end_in)
{
  GimpCoords c_bezier[2][4];
  gint       i;

  g_return_if_fail (matrix != NULL);
  g_return_if_fail (bezier != NULL);
  g_return_if_fail (t_beziers != NULL);
  g_return_if_fail (n_t_beziers != NULL);
  g_return_if_fail (start_in != NULL);
  g_return_if_fail (end_in != NULL);

  /* if the matrix is affine, transform the easy way */
  if (gimp_matrix3_is_affine (matrix))
    {
      GimpCoords *t_bezier;

      t_beziers[0] = g_queue_new ();
      *n_t_beziers = 1;

      t_bezier = g_new (GimpCoords, 1);
      g_queue_push_tail (t_beziers[0], t_bezier);

      for (i = 0; i < 4; i++)
        {
          t_bezier[i] = bezier[i];

          gimp_matrix3_transform_point (matrix,
                                        bezier[i].x,    bezier[i].y,
                                        &t_bezier[i].x, &t_bezier[i].y);
        }

      return;
    }

  /* clip the segment to the near plane */
  clip_bezier (bezier,
               matrix->coeff[2][0],
               matrix->coeff[2][1],
               matrix->coeff[2][2] - GIMP_TRANSFORM_NEAR_Z,
               c_bezier, n_t_beziers,
               start_in, end_in);

  /* transform each of the resulting segments */
  for (i = 0; i < *n_t_beziers; i++)
    {
      t_beziers[i] = g_queue_new ();

      transform_bezier_coords (matrix, c_bezier[i], t_beziers[i], 3);
    }
}

Messung
CHZG
839510092

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417282