HelenOS sources

root/uspace/lib/gfx/src/coord.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gfx_coord_div_rneg
  2. gfx_coord2_add
  3. gfx_coord2_subtract
  4. gfx_coord2_clip
  5. gfx_coord2_project
  6. gfx_span_points_sort
  7. gfx_rect_translate
  8. gfx_rect_rtranslate
  9. gfx_rect_envelope
  10. gfx_rect_clip
  11. gfx_rect_ctr_on_rect
  12. gfx_rect_points_sort
  13. gfx_rect_is_empty
  14. gfx_rect_is_incident
  15. gfx_rect_is_inside
  16. gfx_rect_dims
  17. gfx_pix_inside_rect

/*
 * Copyright (c) 2019 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup libgfx
 * @{
 */
/**
 * @file Coordinates
 */

#include <gfx/coord.h>
#include <macros.h>
#include <stdbool.h>
#include <stddef.h>

/** Divide @a a by @a b and round towards negative numbers.
 *
 * Regular integer division always rounds towards zero. This is not useful
 * e.g. for scaling down, where we always need to round towards negative
 * numbers.
 *
 * @param a Dividend
 * @param b Divisor
 * @return Quotient
 */
gfx_coord_t gfx_coord_div_rneg(gfx_coord_t a, gfx_coord_t b)
{
        if ((a > 0 && b > 0) || (a < 0 && b < 0)) {
                /* Result is non-negative, round towards zero */
                return a / b;
        } else {
                /* Result is negative, round away from zero */
                return (a - b + 1) / b;
        }
}

/** Add two vectors.
 *
 * @param a First vector
 * @param b Second vector
 * @param d Destination
 */
void gfx_coord2_add(gfx_coord2_t *a, gfx_coord2_t *b, gfx_coord2_t *d)
{
        d->x = a->x + b->x;
        d->y = a->y + b->y;
}

/** Subtract two vectors.
 *
 * @param a First vector
 * @param b Second vector
 * @param d Destination
 */
void gfx_coord2_subtract(gfx_coord2_t *a, gfx_coord2_t *b, gfx_coord2_t *d)
{
        d->x = a->x - b->x;
        d->y = a->y - b->y;
}

/** Clip point coordinates to be within a rectangle.
 *
 * @param a Pixel coordinates
 * @param clip Clipping rectangle
 * @param d Place to store clipped coordinates
 */
void gfx_coord2_clip(gfx_coord2_t *a, gfx_rect_t *clip, gfx_coord2_t *d)
{
        gfx_rect_t sclip;
        gfx_coord2_t t;

        gfx_rect_points_sort(clip, &sclip);

        t.x = min(a->x, clip->p1.x - 1);
        t.y = min(a->y, clip->p1.y - 1);

        d->x = max(clip->p0.x, t.x);
        d->y = max(clip->p0.y, t.y);
}

/** Transform coordinates via rectangle to rectangle projection.
 *
 * Transform pixel coordinate via a projection that maps one rectangle
 * onto another rectangle. The source rectangle must have both dimensions
 * greater than one.
 *
 * @param a Pixel coordinates
 * @param srect Source rectangle
 * @param drect Destination rectangle
 * @param d Place to store resulting coordinates.
 */
void gfx_coord2_project(gfx_coord2_t *a, gfx_rect_t *srect, gfx_rect_t *drect,
    gfx_coord2_t *d)
{
        gfx_rect_t sr;
        gfx_rect_t dr;

        gfx_rect_points_sort(srect, &sr);
        gfx_rect_points_sort(drect, &dr);

        d->x = dr.p0.x + (a->x - sr.p0.x) * (dr.p1.x - dr.p0.x - 1) /
            (sr.p1.x - sr.p0.x - 1);
        d->y = dr.p0.y + (a->y - sr.p0.y) * (dr.p1.y - dr.p0.y - 1) /
            (sr.p1.y - sr.p0.y - 1);
}

/** Sort points of a span.
 *
 * Sort the begin and end points so that the begin point has the lower
 * coordinate (i.e. if needed, the span is transposed, if not, it is simply
 * copied).
 *
 * @param s0 Source span start point
 * @param s1 Source span end point
 * @param d0 Destination span start point
 * @param d1 Destination span end point
 */
void gfx_span_points_sort(gfx_coord_t s0, gfx_coord_t s1, gfx_coord_t *d0,
    gfx_coord_t *d1)
{
        if (s0 <= s1) {
                *d0 = s0;
                *d1 = s1;
        } else {
                *d0 = s1 + 1;
                *d1 = s0 + 1;
        }
}

/** Move (translate) rectangle.
 *
 * @param trans Translation offset
 * @param src Source rectangle
 * @param dest Destination rectangle
 */
void gfx_rect_translate(gfx_coord2_t *trans, gfx_rect_t *src, gfx_rect_t *dest)
{
        gfx_coord2_add(&src->p0, trans, &dest->p0);
        gfx_coord2_add(&src->p1, trans, &dest->p1);
}

/** Reverse move (translate) rectangle.
 *
 * @param trans Translation offset
 * @param src Source rectangle
 * @param dest Destination rectangle
 */
void gfx_rect_rtranslate(gfx_coord2_t *trans, gfx_rect_t *src, gfx_rect_t *dest)
{
        gfx_coord2_subtract(&src->p0, trans, &dest->p0);
        gfx_coord2_subtract(&src->p1, trans, &dest->p1);
}

/** Compute envelope of two rectangles.
 *
 * Envelope is the minimal rectangle covering all pixels of both rectangles.
 *
 * @param a First rectangle
 * @param b Second rectangle
 * @param dest Place to store enveloping rectangle
 */
void gfx_rect_envelope(gfx_rect_t *a, gfx_rect_t *b, gfx_rect_t *dest)
{
        gfx_rect_t sa, sb;

        if (gfx_rect_is_empty(a)) {
                *dest = *b;
                return;
        }

        if (gfx_rect_is_empty(b)) {
                *dest = *a;
                return;
        }

        /* a and b are both non-empty */

        gfx_rect_points_sort(a, &sa);
        gfx_rect_points_sort(b, &sb);

        dest->p0.x = min(sa.p0.x, sb.p0.x);
        dest->p0.y = min(sa.p0.y, sb.p0.y);
        dest->p1.x = max(sa.p1.x, sb.p1.x);
        dest->p1.y = max(sa.p1.y, sb.p1.y);
}

/** Compute intersection of two rectangles.
 *
 * If the two rectangles do not intersect, the result will be an empty
 * rectangle (check with gfx_rect_is_empty()). The resulting rectangle
 * is always sorted. If @a clip is NULL, no clipping is performed.
 *
 * @param rect Source rectangle
 * @param clip Clipping rectangle or @c NULL
 * @param dest Place to store clipped rectangle
 */
void gfx_rect_clip(gfx_rect_t *rect, gfx_rect_t *clip, gfx_rect_t *dest)
{
        gfx_rect_t srect, sclip;

        if (clip == NULL) {
                *dest = *rect;
                return;
        }

        gfx_rect_points_sort(rect, &srect);
        gfx_rect_points_sort(clip, &sclip);

        dest->p0.x = min(max(srect.p0.x, sclip.p0.x), sclip.p1.x);
        dest->p0.y = min(max(srect.p0.y, sclip.p0.y), sclip.p1.y);
        dest->p1.x = max(sclip.p0.x, min(srect.p1.x, sclip.p1.x));
        dest->p1.y = max(sclip.p0.y, min(srect.p1.y, sclip.p1.y));
}

/** Center rectangle on rectangle.
 *
 * Translate rectangle @a a so that its center coincides with the
 * center of rectangle @a b, saving the result in @a dest.
 *
 * @param a Rectnagle to translate
 * @param b Rectangle on which to center
 * @param dest Place to store resulting rectangle
 */
void gfx_rect_ctr_on_rect(gfx_rect_t *a, gfx_rect_t *b, gfx_rect_t *dest)
{
        gfx_coord2_t adim;
        gfx_coord2_t bdim;

        gfx_rect_dims(a, &adim);
        gfx_rect_dims(b, &bdim);

        dest->p0.x = b->p0.x + bdim.x / 2 - adim.x / 2;
        dest->p0.y = b->p0.y + bdim.y / 2 - adim.y / 2;

        dest->p1.x = dest->p0.x + adim.x;
        dest->p1.y = dest->p0.y + adim.y;
}

/** Sort points of a rectangle.
 *
 * Shuffle around coordinates of a rectangle so that p0.x < p1.x and
 * p0.y < p0.y.
 *
 * @param src Source rectangle
 * @param dest Destination (sorted) rectangle
 */
void gfx_rect_points_sort(gfx_rect_t *src, gfx_rect_t *dest)
{
        gfx_span_points_sort(src->p0.x, src->p1.x, &dest->p0.x, &dest->p1.x);
        gfx_span_points_sort(src->p0.y, src->p1.y, &dest->p0.y, &dest->p1.y);
}

/** Determine if rectangle contains no pixels
 *
 * @param rect Rectangle
 * @return @c true iff rectangle contains no pixels
 */
bool gfx_rect_is_empty(gfx_rect_t *rect)
{
        return rect->p0.x == rect->p1.x || rect->p0.y == rect->p1.y;
}

/** Determine if two rectangles share any pixels
 *
 * @param a First rectangle
 * @param b Second rectangle
 * @return @c true iff rectangles share any pixels
 */
bool gfx_rect_is_incident(gfx_rect_t *a, gfx_rect_t *b)
{
        gfx_rect_t r;

        gfx_rect_clip(a, b, &r);
        return !gfx_rect_is_empty(&r);
}

/** Return true if rectangle @a a is contained in rectangle @a b.
 *
 * @param a Inside rectangle
 * @param b Outside rectangle
 * @return @c true iff @a a is (non-strictly) inside @a b
 */
bool gfx_rect_is_inside(gfx_rect_t *a, gfx_rect_t *b)
{
        gfx_rect_t sa;
        gfx_rect_t sb;

        gfx_rect_points_sort(a, &sa);
        gfx_rect_points_sort(b, &sb);

        if (sa.p0.x < sb.p0.x || sa.p0.y < sb.p0.y)
                return false;

        if (sa.p1.x > sb.p1.x || sa.p1.y > sb.p1.y)
                return false;

        return true;
}

/** Get rectangle dimensions.
 *
 * Get a vector containing the x, y dimensions of a rectangle. These are
 * always nonnegative.
 *
 * @param rect Rectangle
 * @param dims Place to store dimensions
 */
void gfx_rect_dims(gfx_rect_t *rect, gfx_coord2_t *dims)
{
        gfx_rect_t srect;

        gfx_rect_points_sort(rect, &srect);
        gfx_coord2_subtract(&srect.p1, &srect.p0, dims);
}

/** Return true if pixel at coordinate @a coord lies within rectangle @a rect. */
bool gfx_pix_inside_rect(gfx_coord2_t *coord, gfx_rect_t *rect)
{
        gfx_rect_t sr;

        gfx_rect_points_sort(rect, &sr);

        if (coord->x < sr.p0.x || coord->y < sr.p0.y)
                return false;

        if (coord->x >= sr.p1.x || coord->y >= sr.p1.y)
                return false;

        return true;
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub