/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
    */

/** @defgroup contour The contour library
 * @ingroup libcgencommon
 *
 * The contour library contains functionality for handling 2D vector shapes. Intersection,
 * union, expansion, area/circumference calculation, distance computation are the key
 * features.
 *
 * What is a *contour*?
 * --------------------
 *
 * We define the concept of a *contour* (embodied in the Contour class) as a
 * closed 2D shape. It has vertices and edges, where edges can be straight sections
 * or cubic bezier curves. Contours are stored as lists (well, vectors) of their edges.
 *
 * The library can handle contours that have *holes* in them, or that have multiple
 * disjoint parts (even inside a hole). We always try to normalize contours
 * such that their edges do not cross any other edge.
 * Edges are ordered in the contour - *clockwise* for positive contour outlines,
 * while counterclockwise for outlines representing holes. Clockwiseness is defined
 * such that the y coordinate grows _downward_. This convention is used everywhere
 * as throughout the comments.
 *
 * Features
 * --------
 *
 * - The main feature of the library is to be able to create union and intersection
 * of contours; subtraction and xor operation is also possible.
 * - A number of auxiliary operations
 * are defined, such as area, centroid location and circumference length calculation,
 * tangent fitting and various cuts.
 * - Naturally linear transformations (shift, rotate, scale, etc.) are supported too.
 * - The library uses the C++11 move schematics extensively.
 * - The library can *expand* a contour by a given pixels, that is generate
 * a contour that is this many pixels wider (or narrower for negative values),
 * such that the resulting contour is sane - its edges do not cross each other.
 * (In general this is called curve offsetting, but for closed curves this really
 * becomes expansion or shrinking.)
 * - Another way to expand a shape (called Expand2D) takes two parameters (x and y)
 * and creates a shape around the original such that an x-y rectangle just fits
 * in between at any given place. In other words, placing a 2*x - 2*y rectangle
 * centered anywhere outside the expanded shape will not overlap with the original.
 * - The distance of two contours (along with the closest points) can be determined.
 * - Especially important for layout typography, the library can determine, how much a
 * one of two shapes has to be moved up or down to touch another shape.
 *
 * See the definition of the Contour class for a detailed description of the
 * features.
 *
 * The library also contains a Path class which is represents a set of
 * (not necessarily connected) edges, which may or may not be closed.
 * It supports basic transformations and drawing.
 * You can also convert it to a Contour (assuming it is a closed path).
 *
 * Terminology
 * -----------
 *
 * - *pos* usually refers to a location on the edge and is between [0..1).
 *   The start of the edge is position zero, the end of it is position 1, but this
 *   is not used, as we refer to the end of an edge as the start of the next edge.
 * - *Crosspoints* are points of edges meeting.
 * - *rays* are defined in the context of a crosspoint. A ray is a part of an edge
 *   coming to or leaving from a crosspoint (called incoming/outgoing rays, respectively).
 *   For example, two straight crossing edges have four rays, two incoming and two outgoing.
 *   If three edges cross at the same point, we have 6 rays. If an edge `E` crosses a vertex `V`
 *   of the other polygon, we still have 4 rays: one incoming and outgoing for the edge;
 *   an incoming ray for the edge leading to `V` and an outgoing ray for the edge after `V`.
 *   A ray can be curvy, if its edge is a bezier.
 * - *Walking* is the procedure used to calculate the union/intersection of two shapes.
 *   We calculate all crosspoints and start walking around one of the contours. During the walk
 *   we stop by each crosspoint and see if we have to switch to the other contour. Finally we get
 *   back to the staring point. Then we can start an additional walk if relevant crosspoints have
 *   not been visited - in which case the resulting contour will not be contiguous, that is it will
 *   contain multiple disjoint shapes. (Some of which may turn out to be holes inside other
 *   shapes.)
 * - A *tangent* for our purposes is a line that touches a shape (or edge) only at a single point
 *   (Except for straight lines, where the tangent _lies on_ the edge.)
 *   Class Contour and Edge have functions that take that single point as input and provide another point on the
 *   tangent line some distance away. We can speak of a *forward* point, which is when we start on the
 *   tangent line towards the end of the edge or a *backward* point which is towards the start of the edge.
 *   We also call these 'next' and 'prev' tangent points, respectively.
 *   Now, a few more details here
 *       one point.
 *     - If we take a tangent at a point on a concave edge (say the inner circle of a crescent moon)
 *       the tangent will even locally be inside the shape.
 *     - If we take a tangent at a point that is on a straight edge, naturally the tangent line will
 *       lien on the straight edge.
 *     - If we take a tangent at a vertex, which is not smooth (has an angle), there are actually two
 *       different tangent lines, one for each edge. In this case the forward and backward tangent points
 *       are returned form the edge subsequent or preceding to the vertex, respectively.
 * - *Untangling* means the process of creating a shape, whose edges do not cross each other. Normally
 *   contours in class Contour have no edges crossing. There are two rules of what part of a tangled
 *   list of edges shall we keep during untangling, called winding rules.
 *   For both fill rules, whether or not a point is included
 *   in the resulting shape is determined by taking a ray from that point to infinity and looking at intersections
 *   with the original shape's contour. The ray can be in any direction, as long as it doesn't pass through the
 *   end point of an edge or have a tricky intersection such as intersecting tangent to the path.
 *     - *Nonzero* rule: If the original shape crosses the ray from left-to-right, counts +1.
 *       If the path crosses the ray from right to left, counts -1. (Left and right are determined from the
 *       perspective of looking along the ray from the starting point.)
 *       If the total count is non-zero, the point will be included in the untangled shape.
 *     - *Evenodd* rule: Counts the total number of intersections, without regard to the orientation of the contour.
 *       If the total number of intersections is odd, the point will be included in the untangled shape.
 */

/** @defgroup contour_files Files of the contour library
 * @ingroup contour
 * @brief These files constitute the contour library.
 * @file contour_basics.h The basic definitions for geometry
 * calculations (the contour library).
 * @ingroup contour_files
 */

/** @defgroup contour_internal Internal elements of the contour library.
 * @ingroup contour
 * @brief These elements are internal to the contour library and are presented for understanding the inner workings.
 */

#if !defined(CONTOUR_BASICS_H)
#define CONTOUR_BASICS_H

#include <cfloat>
#include <utility>
#include <cassert>
#include <cstddef>
#include <limits>
#include <algorithm>
#include <optional>
#include <string>
#include <array>

#ifndef _ASSERT
#define  _ASSERT(A) assert(A)
#elif !defined(_DEBUG) && !defined(NDEBUG)
#define NDEBUG
#endif

#ifdef __has_cpp_attribute
#if __has_cpp_attribute(fallthrough)
#define FALLTHROUGH [[fallthrough]]
#else
#define FALLTHROUGH
#endif
#else
#define FALLTHROUGH
#endif

#define _USE_MATH_DEFINES
#include <cmath>
#ifndef M_PI
/** The value of pi. */
#define M_PI 3.14159265358979323846
#endif

#ifdef DBL_DECIMAL_DIG
#define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else
#ifdef DECIMAL_DIG
#define OP_DBL_Digs (DECIMAL_DIG)
#else
#define OP_DBL_Digs (DBL_DIG + 3)
#endif
#endif


/** The `contour` namespace holds all the contour library
 */
namespace contour {

/** Enum describing the relation of two closed contours.
* @ingroup contour
*/
enum EContourRelationType {
    REL_OVERLAP=0,          ///< They overlap, but each has parts not covered by the other.
    REL_A_IS_EMPTY,         ///< The first contour is empty, the second is not.
    REL_B_IS_EMPTY,         ///< The second contour is empty, the first is not.
    REL_BOTH_EMPTY,         ///< Both contours are empty.
    REL_A_INSIDE_B,         ///< The first contour is fully inside the second, which has parts not overlapping with the first.
    REL_B_INSIDE_A,         ///< The second contour is fully inside the first, which has parts not overlapping with the first.
    REL_SAME,               ///< The two contours are identical (cover exactly the same area).
    REL_APART,              ///< Two non-empty contours that has no intersecting area, and none is around the other.
    REL_A_IN_HOLE_OF_B,     ///< The first contour is fully inside a hole of the second.
    REL_B_IN_HOLE_OF_A,     ///< The second contour is fully inside a hole of the first.
    REL_IN_HOLE_APART       ///< The two contours have no overlapping area, but each as a part in a hole of the other.
};

/** @addtogroup contour_internal
 * @{
 */

/** Gives the maximum value for a variable
 * \param real The variable we want the maximum value of
 * \return The maximum value for `real`
 */
#define MaxVal(real) (std::numeric_limits<decltype(real)>::max())
/** The maximum coordinate for contour library
 */
#define CONTOUR_INFINITY (std::numeric_limits<double>::max())

//other helpers
/** We quantize to this granularity to avoid underflow.
 *
 * The contour library cannot distinguish between distances smaller than this.
 * Also, any numerical result will be treated as equal if within this distance.
 * test_xxx() routines all rely on this constant for precision.
 */
constexpr double SMALL_NUM = 1e-5;
template <typename real>
constexpr bool test_zero(real n) noexcept { return -SMALL_NUM<=n && n<=SMALL_NUM; } ///< True if `n` is sufficiently close to zero, uses SMALL_NUM.
template <typename real1, typename real2>
constexpr bool test_equal(real1 n, real2 m) noexcept {return test_zero(n-m);} ///< True if `n` and `m` are sufficiently close to each other, uses SMALL_NUM
template <typename real1, typename real2>
constexpr bool test_smaller(real1 n, real2 m) noexcept {return n<m-SMALL_NUM;} ///< True if `n` is *really* smaller than `m`, not just by epsilon, uses SMALL_NUM
template <typename real>
constexpr bool test_positive(real n) noexcept {return n >= SMALL_NUM;} ///< True if `n` is sufficiently larger than zero, uses SMALL_NUM

template <typename real> constexpr real constexpr_fabs(real a) noexcept { return a<0 ? -a : a; }

/** Returns the value closer to zero.
 */
template <typename real> constexpr real minabs(real a, real b) noexcept {return constexpr_fabs(a)<constexpr_fabs(b) ? a : b;}

/** Returns -1, 0 or +1 indicating the sign of `a`.
 */
template <typename real> constexpr int fsign(real a) noexcept {return a>0 ? +1 : a<0 ? -1 : 0;}

/** Returns true if the relation means that the two contours
 * have non empty overlapping area.
 */
constexpr bool result_overlap(EContourRelationType t) noexcept {return t==REL_OVERLAP || t==REL_A_INSIDE_B || t==REL_B_INSIDE_A || t==REL_SAME;}
/** Returns the relation with the role of the two contours
 * swapped.
 */
inline EContourRelationType switch_side(EContourRelationType t) noexcept
{
    switch (t) {
    default: _ASSERT(0); FALLTHROUGH;
    case REL_IN_HOLE_APART:
    case REL_BOTH_EMPTY:
    case REL_OVERLAP:
    case REL_APART:
    case REL_SAME:            return t;

    case REL_A_IS_EMPTY:      return REL_B_IS_EMPTY;
    case REL_B_IS_EMPTY:      return REL_A_IS_EMPTY;
    case REL_B_INSIDE_A:      return REL_A_INSIDE_B;
    case REL_A_INSIDE_B:      return REL_B_INSIDE_A;;
    case REL_B_IN_HOLE_OF_A:  return REL_A_IN_HOLE_OF_B;
    case REL_A_IN_HOLE_OF_B:  return REL_B_IN_HOLE_OF_A;
    }
}

/** @} addgroup contour_internal*/

struct TRMatrix;

/** The basic class for a (2D, floating point) coordinate or vector.
* @ingroup contour
 *
 * Note that in all cases when we talk about clockwiseness in this space,
 * we envision it such that the y coordinate grows _downward_, which is
 * the opposite of mathematical definition.
 * Also for angles, direction zero is thus to the *right*, a degree of 45,
 * is down-right, a degree of 90 is downward, etc.
 */

struct XY {
    double x = 0; ///< The x coordinate.
    double y = 0; ///< The y coordinate.
    constexpr XY &   operator +=(const XY &wh)      noexcept {x+=wh.x; y+=wh.y; return *this;}    ///< Shifts the point by `wh`.
    constexpr XY &   operator -=(const XY &wh)      noexcept {x-=wh.x; y-=wh.y; return *this;}    ///< Shifts the point by `-wh`.
    constexpr XY     operator +(const XY &wh) const noexcept {return XY(x+wh.x, y+wh.y);}         ///< Returns the point shifted by `wh`, keeping the original.
    constexpr XY     operator -(const XY &wh) const noexcept {return XY(x-wh.x, y-wh.y);}         ///< Returns the point shifted by `-wh`, keeping the original.
    constexpr double DotProduct(const XY &B) const  noexcept {return x*B.x+y*B.y;}                ///< Returns dot product of the point as a vector, with `B`.
    constexpr XY     operator *(double scale) const noexcept {return XY(x*scale, y*scale);}       ///< Returns a scaled version of the point.
    constexpr XY &   operator *=(double scale)      noexcept {x*=scale; y*=scale; return *this;}  ///< Scales the point.
    constexpr XY     operator /(double scale) const noexcept {return XY(x/scale, y/scale);}       ///< Returns a scaled version of the point.
    constexpr XY &   operator /=(double scale)      noexcept {x/=scale; y/=scale; return *this;}  ///< Scales the point.
    constexpr double PerpProduct(const XY &B) const noexcept {return x*B.y - y*B.x;}              ///< This the dot product of transpose(point) and `B`.
              double length(void) const             noexcept {return sqrt(x*x+y*y);}              ///< Length of vector or distance of point from origin.
    constexpr double length_sqr(void) const         noexcept {return x*x+y*y;}                    ///< Square of the length of vector.
    constexpr bool   operator ==(const XY& p) const noexcept {return x==p.x && y==p.y;}
    constexpr XY	 operator -() const             noexcept {return XY(-x, -y);}
    constexpr bool   operator <(const XY& p) const  noexcept {return x!=p.x ? x<p.x : y<p.y;}     ///< Cheap comparison so `XY` can be inserted into ordered sets.
    constexpr bool   test_equal(const XY& p) const  noexcept { return contour::test_equal(x, p.x) && contour::test_equal(y, p.y); } ///< Tests if point is close to `p`, uses SMALL_NUM.
    constexpr bool   test_equal(const XY& p, double t) const noexcept { return constexpr_fabs(x-p.x)<=t && constexpr_fabs(y-p.y)<=t; } ///< Tests if point is close to `p`, uses t
    constexpr bool   test_smaller(const XY& p) const noexcept{return contour::test_equal(x, p.x) ? contour::test_smaller(y, p.y) : contour::test_smaller(x, p.x);} ///< Tests if point is significantly smaller than `p`, uses SMALL_NUM.
    constexpr XY     Rotate90CW() const             noexcept {return XY(-y, x);}  ///< Rotate 90 degrees clockwise (in a space where y grows "downwards").
    constexpr XY     Rotate90CCW() const            noexcept {return XY(y, -x);}  ///< Rotate 90 degrees counter-clockwise (in a space where y grows "downwards").
    constexpr XY &   Rotate(double cos, double sin) noexcept {const double X=x; x=X*cos-y*sin; y=X*sin+y*cos; return *this;} ///< Free clockwise rotate with user-supplied `sin` and `cos` of the angle (in a space where y grows "downwards").
    constexpr XY &   RotateAround(const XY&c, double cos, double sin) noexcept
	    {const double X=x-c.x, Y=y-c.y; x=X*cos-Y*sin+c.x; y=X*sin+Y*cos+c.y; return *this;} ///< Free clockwise rotate around `c` with user-supplied `sin` and `cos` of the angle (in a space where y grows "downwards").
    constexpr XY &   SwapXY()                       noexcept {std::swap(x,y); return *this;}
              XY &   Round()                        noexcept {x = floor(x+.5); y=floor(y+.5); return *this;} ///< Round coordinates to closest integer.
              XY &   RoundUp()                      noexcept {x = ceil(x); y=ceil(y); return *this;}         ///< Round coordinates to integer (away from zero).
              XY &   RoundDown()                    noexcept {x = floor(x); y=floor(y); return *this;}       ///< Round coordinates to integer (towards zero).
    constexpr XY &   Scale(const XY &sc)            noexcept {x*=sc.x; y*=sc.y; return *this;}               ///< Scale the two coordinates separately.
              XY &   Normalize()                    noexcept {const double l = length(); if (l) {x/=l; y/=l;} return *this;}  ///< Return a unit-length scaled version.
              double Distance(const XY &p) const    noexcept {return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y));}  ///< Distance of us to `p`.
    constexpr double DistanceSqr(const XY &p) const noexcept {return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);}        ///< Square of distance to `p`.
    constexpr XY     ProjectOntoLine(const XY&A, const XY&B) const noexcept {if (A.test_equal(B)) return A; const XY d = A-B; return B + d*d.DotProduct(*this-B)/d.length_sqr();} ///< Return closest point of the (infinite) line (defined by `A` and `B`) to us.
    constexpr XY     Transform(double xx, double xy, double yx, double yy, double dx, double dy) const noexcept
                                          {return XY(x*xx + y*xy + dx, x*yx + y*yy + dy);} ///< Transform via the transformation linear matrix.
    constexpr XY &   Apply(const TRMatrix &M) noexcept;
    constexpr double operator[](unsigned i) const noexcept { return i ? y : x; }
    constexpr double operator[](bool i) const noexcept { return i ? y : x; }
    constexpr double &operator[](unsigned i) noexcept { return i ? y : x; }
    constexpr double &operator[](bool i) noexcept { return i ? y : x; }
    std::string Dump(bool precise) const;
    bool IsSane() const noexcept { return !std::isnan(x) && !std::isnan(y); } //we consider infinite and subnormal values OK
};

constexpr XY operator*(double a, const XY &xy) noexcept {return xy*a;} ///< Return a scaled version of the point `xy`. Enables writing the scalar before the point.

/** Enum describing the relation of a contour and a point.
 * @ingroup contour
 */
enum EPointRelationType {
    WI_OUTSIDE=0, ///< The point is fully outside the contour.
    WI_INSIDE,    ///< The point is fully inside the contour.
    WI_ON_EDGE,   ///< The point is on (the middle of) an edge.
    WI_ON_VERTEX, ///< The point is exactly at a vertex.
    WI_IN_HOLE    ///< The point is in a hole of the contour.
};

/** Returns true, if the point is inside or at the edge of the contour.
 * @ingroup contour_internal
*/
constexpr bool inside(EPointRelationType t) noexcept {return t!=WI_OUTSIDE && t!=WI_IN_HOLE;}

/** The structure for a one-dimensional range.
 * @ingroup contour
 *
 * Note that if `from` is larger than `till`, we consider that an "invalid"
 * range. If they are the same, the range is "empty".
 */
struct Range {
    double from;  ///< The lower (smaller) end of the range.
    double till;  ///< The higher (larger) end of the range.
    Range() noexcept = default;
    explicit constexpr Range(bool) noexcept : from(MaxVal(from)), till(-MaxVal(till)) {}
    constexpr Range(double s, double d) noexcept : from(s), till(d) {}
    void MakeInvalid() noexcept {from = MaxVal(from); till = -MaxVal(till);}
    constexpr bool IsInvalid() const noexcept {return from == CONTOUR_INFINITY && till == -CONTOUR_INFINITY;}
    bool IsSane() const noexcept { return !std::isnan(from) && !std::isnan(till); } //we consider infinite and subnormal values OK
    constexpr bool Overlaps(const struct Range &r, double gap) const  noexcept  ///< True if the two ranges at least touch.
        {return from<=r.till+gap && r.from <= till+gap;}
    constexpr bool Overlaps(const struct Range &r) const  noexcept  ///< True if the two ranges at least touch.
        {return from<=r.till && r.from <= till;}
    constexpr bool HasZeroOverlap(const struct Range& r) const  noexcept  ///< True if the two ranges at most touch, but have zero intersection
        { return from>=r.till || r.from>=till; }
    Range &operator+=(double a) noexcept
        {if (from>a) from=a; if (till<a) till=a; return *this;}  ///< Expands the range to include the value `a`. Note, this is _not_ a shift operation.
    Range &operator+=(const Range &a) noexcept
        {if (from>a.from) from=a.from; if (till<a.till) till=a.till; return *this;}  ///<Expands the range to include all points of the range `a`. Note, this is _not_ a shift operation.
    Range &operator-=(const Range &a) noexcept {
        if (a.IsWithin(from)) from=a.till;
        if (a.IsWithin(till)) till=a.from;
        return *this;}                                          ///< Cuts range `a` out of the range. May render the range invalid. Note, this is _not_ a shift operation.
    Range &operator*=(const Range &a) noexcept
        {if (from<a.from) from=a.from; if (till>a.till) till=a.till; return *this;} ///< Takes the intersection with range `a`. Note, this is _not_ a scale operation.
    constexpr Range operator+(const Range &a) const noexcept
        {return Range(a.from<from ? a.from : from, a.till<till ? till : a.till);}  ///< Returns the range expanded to include `a`. Note, this is _not_ a shift operation.
    constexpr Range operator*(const Range &a) const noexcept
        {return Range(a.from<from ? from : a.from, a.till<till ? a.till : till);}  ///< Returns the intersection with range `a`. Note, this is _not_ a scale operation.

    constexpr EPointRelationType IsWithin(double p) const noexcept {
		return (p==from || p == till) ? WI_ON_VERTEX :from<p && p<till ? WI_INSIDE : WI_OUTSIDE;
    }                                                                    ///< Checks if a value is within, at the end or outside of a range.
    constexpr bool IsWithinBool(double p) const noexcept {return from<=p && p<=till;}       ///< Checks if a value is inside/at edge of the range or outside.
    Range &Shift(double a) noexcept {from+=a; till+=a; return *this;}             ///< Shift the range by `a`.
    Range &Scale(double a) noexcept { from *= a; till *= a; if (a<0) std::swap(from, till); return *this; } ///<Scale the range by `a`.
    Range &Expand(double a) noexcept { from -= a; till += a; return *this; }            ///< Expand the range at both ends by a.
    constexpr Range CreateShifted(double a) const noexcept { return Range(from + a, till +a); }             ///< Create a shifted range the range by `a`.
    constexpr Range CreateScaled(double a) const noexcept { return a>0 ? Range(from*a, till*a) : Range(till*a, from*a); } ///<Create a scaled range by `a`.
    constexpr Range CreateExpanded(double a) const noexcept { return Range(from - a, till + a); }            ///< Create an expanded range at both ends by a.
    constexpr bool HasValidFrom() const noexcept {return from != CONTOUR_INFINITY;}         ///< Checks if the `from` member is valid.
    constexpr bool HasValidTill() const noexcept {return till != -CONTOUR_INFINITY;}        ///< Checks if the `till` member is valid.
    constexpr double Spans() const noexcept {return till-from;}                             ///< Calculates the width of the range.
    constexpr double MidPoint() const noexcept {return (from+till)/2;}                      ///< Returns the midpoint of the range - the average of `from` and `till`.
    constexpr bool operator <(const Range &r) const noexcept { return (till==r.till) ? from<r.from : till<r.till; } ///< Cheap comparison for sorting.
    constexpr bool operator ==(const Range &r) const noexcept {return from==r.from && till==r.till;}
    Range & Round()       noexcept {from = floor(from+.5); till=floor(till+.5); return *this;}  ///< Round both ends to closest integer.
    Range & RoundUp()     noexcept {from = ceil(from); till=ceil(till); return *this;}          ///< Round both ends upward (towards larger integer)
    Range & RoundDown()   noexcept {from = floor(from); till=floor(till); return *this;}        ///< Round both ends downward (towards smaller integer)
    Range & RoundWider()  noexcept {from = floor(from); till=ceil(till); return *this;}         ///< Round ends outward (`from` downward, `till` upward)
    Range & RoundCloser() noexcept {from = ceil(from); till=floor(till); return *this;}         ///< Round ends inward (`from` upward, `till` downward)

    constexpr Range operator -() const noexcept { return Range(-till, -from); }  ///<Unary negation, make both ends negative - but maintain correct ordering from<till

    double Distance(double a) const noexcept {return minabs(from-a,a-till);}  ///< Returns the distance between a range and a point. Negative result if point is inside the range.
    EContourRelationType RelationTo(const Range &c) const noexcept {
        if (IsInvalid()) return c.IsInvalid() ? REL_BOTH_EMPTY : REL_A_IS_EMPTY;
        if (c.IsInvalid()) return REL_B_IS_EMPTY;
        if (from>=c.till || c.from>=till) return REL_APART;
        if (from<=c.from && till>=c.till) return REL_B_INSIDE_A;
        if (c.from<=from && c.till>=till) return REL_A_INSIDE_B;
        return REL_OVERLAP;
    }  ///< Returns the relation of the two ranges, e.g., overlap, inside, apart, etc.
    /** Returns the relative position of 'v' in from->till.
     * @returns negative if v<from, 0 if v==from, 1 if v==till, and 1+ if till<v.
     * For invalid or empty ranges we return something undetermined.*/
    constexpr double Pos(double v) const noexcept {
        if (from>=till) return 0;
        return (v-from)/Spans();
    }
    constexpr double operator[](unsigned i) const noexcept { return i ? till : from; }
    constexpr double operator[](bool i) const noexcept { return i ? till : from; }
    constexpr double& operator[](unsigned i) noexcept { return i ? till : from; }
    constexpr double& operator[](bool i) noexcept { return i ? till : from; }
};

/** A basic rectangle structure.
 * @ingroup contour
 *
 * Note that this rectangle is composed of two ranges, hence all time 4 double values
 * are used to describe a rectangle, we start with the 2 x coordinates followed by the two
 * y coordinates, unlike to many other systems, which start with the upper left corner and
 * either continue by width/height or the lower-right coordinate.
 *
 * An empty Block is called *invalid* and is represented by at least one of the ranges
 * being invalid (that is `till < from`).
 */
struct Block {
    struct Range x;
    struct Range y;
    Block() noexcept = default;
    explicit Block(bool b) noexcept { _ASSERT(!b);  (void)(b);  MakeInvalid(); } //make sure we call it with 'false' that reminds me that this invalidates
    constexpr Block(double sx, double dx, double sy, double dy) noexcept :
        x(sx<dx ? sx : dx, sx<dx ? dx : sx), y(sy<dy ? sy : dy, sy<dy ? dy : sy) {}
    constexpr Block(const XY &ul, const XY &dr) noexcept :
        x(ul.x<dr.x ? ul.x : dr.x, ul.x<dr.x ? dr.x : ul.x), y(ul.y<dr.y ? ul.y : dr.y, ul.y<dr.y ? dr.y : ul.y) {}
    constexpr Block(const Range &X, const Range &Y) noexcept : x(X), y(Y) {}
    constexpr Block(const Block &) noexcept = default;
    void MakeInvalid() noexcept {x.MakeInvalid(); y.MakeInvalid();}           ///< Empty the Block.
    constexpr bool IsInvalid() const noexcept {return x.IsInvalid() || y.IsInvalid();}  ///< Check if Block is empty or not.
    bool IsSane() const noexcept { return x.IsSane() && y.IsSane(); } //we consider infinite and subnormal values OK
    /** Comparison operator required for set ordering.
     *
     * Blocks whose y coordinate is larger are counted as larger here.
     * Note that this definition improves performance when checking overlaps
     * between a large number of Blocks (lower blocks come later).
     */
    constexpr bool operator <(const struct Block &b) const noexcept {
        return (y.till != b.y.till) ? y.till < b.y.till :
               (x.till != b.x.till) ? x.till < b.x.till :
               (x.from != b.x.from) ? x.from < b.x.from :
                y.from < b.y.from;
    }
    constexpr bool operator == (const struct Block &b) const noexcept {return x==b.x && y==b.y;}
    constexpr bool Overlaps(const struct Block &b, double gap) const noexcept
        {return x.Overlaps(b.x, gap) && y.Overlaps(b.y, gap);} ///< Sees if the two blocks have at least `gap` vertical and horizontal distance between them.
    constexpr bool Overlaps(const struct Block &b) const noexcept
        {return x.Overlaps(b.x) && y.Overlaps(b.y);} ///< Sees if the two blocks have at least `gap` vertical and horizontal distance between them.
    constexpr bool HasZeroOverlap(const struct Block &b) const noexcept
        {return x.HasZeroOverlap(b.x) || y.HasZeroOverlap(b.y);} ///< True if the two blocks have an empty intersection. They may touch.
    constexpr XY UpperLeft(void) const noexcept
        {return XY(x.from, y.from);}  ///< Returns the coordinates of the upper-left corner. The one with the smaller y coordinate.
    constexpr XY LowerRight(void) const noexcept
        {return XY(x.till, y.till);}  ///< Returns the coordinates of the lower-right corner. The one with the larger y coordinate.
    constexpr XY UpperRight(void) const noexcept
        {return XY(x.till, y.from);}  ///< Returns the coordinates of the upper-right corner. The one with the smaller y coordinate.
    constexpr XY LowerLeft(void) const noexcept
        {return XY(x.from, y.till);}  ///< Returns the coordinates of the lower-left corner. The one with the larger y coordinate.
    constexpr XY Centroid(void) const noexcept
        {return XY(x.MidPoint(), y.MidPoint());}  ///< Returns the geometric center of the rectangle.
    constexpr XY Spans(void) const noexcept
        {return XY(x.Spans(), y.Spans());}        ///< Returns the witdth and the height of the rectangle.
    constexpr double GetArea() const noexcept {return IsInvalid() ? 0 : x.Spans()*y.Spans();}  ///< Returns the size of the area occupied by the rectangle.
    constexpr double GetCircumference() const noexcept {return IsInvalid() ? 0 : 2*(x.Spans()+y.Spans());} ///< Returns the length of the circumference of the rectangle.
    constexpr EPointRelationType IsWithin(const XY &p) const noexcept {
        return (x.IsWithin(p.x) == WI_OUTSIDE   || y.IsWithin(p.y) == WI_OUTSIDE)   ? WI_OUTSIDE :
               (x.IsWithin(p.x) == WI_INSIDE    && y.IsWithin(p.y) == WI_INSIDE)    ? WI_INSIDE :
               (x.IsWithin(p.x) == WI_ON_VERTEX && y.IsWithin(p.y) == WI_ON_VERTEX) ? WI_ON_VERTEX : WI_ON_EDGE;
    } ///< Checks if a point is inside the block, on its edges, vertices or outside.
    constexpr bool IsWithinBool(const XY &p) const noexcept {return x.IsWithinBool(p.x) && y.IsWithinBool(p.y);} ///< Returns true if the point is inside or on an edge/vertex of the Block.
    Block & operator +=(const XY &p) noexcept
        {x += p.x; y += p.y; return *this;}  ///< Expands the rectangle to include `p`. Note this is _not_ a shift operation.
    Block & operator +=(const Block &b) noexcept
        {x += b.x; y += b.y; return *this;}  ///< Expands the rectangle to include `b`. Note this is _not_ a shift operation.
    constexpr Block operator+(const Block &b) const noexcept
        {return Block(x+b.x, y+b.y);}        ///< Union: Return the smallest rectangle containing both `this` and `b`.
    constexpr Block operator*(const Block &b) const noexcept
        {return Block(x*b.x, y*b.y);}        ///< Intersect: Return the largest rectangle inside both `this` and `b`.
    Block &Shift(const XY &a) noexcept
        {x.Shift(a.x); y.Shift(a.y); return *this;}                                         ///< Shift the rectangle.
    constexpr Block CreateShifted(const XY &a) const noexcept
        {return Block(x.CreateShifted(a.x), y.CreateShifted(a.y));}                         ///< Create a shifted copy of the rectangle.
    Block &Scale(const XY &sc) noexcept {x.Scale(sc.x); y.Scale(sc.y); return *this;}       ///< Scale the rectangle differently in the two dimensions.
    Block &Scale(double sc) noexcept { x.Scale(sc); y.Scale(sc); return *this; }            ///< Scale the rectangle uniformly in both dimensions.
    constexpr Block CreateScaled(double sc) const noexcept
        { return Block(x.CreateScaled(sc), y.CreateScaled(sc)); }                           ///< Create a uniformly scaled (in both dimensions) copy.
    Block &Expand(double a) noexcept {x.Expand(a); y.Expand(a); return *this;}              ///< Expand the rectangle in all 4 directions by `a`. Sides grow by `2*a`.
    constexpr Block CreateExpand(double a) const noexcept
        {return Block(x.CreateExpanded(a), y.CreateExpanded(a)); }                          ///< Return an expanded version, keeping the original const.
    Block &Expand2D(const XY &gap) noexcept {x.Expand(gap.x); y.Expand(gap.y); return *this;}///< Expand the Block differently in both directions. Horizontal sides grow by `2*gap.x', vertical sides grow by `2*gap.y`.
    constexpr Block CreateExpand2D(const XY &gap) const noexcept
        {return Block(x.CreateExpanded(gap.x), y.CreateExpanded(gap.y));}                   ///< Return a version of the Block expanded differently in the two dimensions.
    Block &SwapXY() noexcept       {std::swap(x,y); return *this;}                    ///< Swap x and y coordinates.
    Block & Round() noexcept       {x.Round(); y.Round(); return *this;}              ///< Round all corners to closest integer coordinates.
    Block & RoundUp() noexcept     {x.RoundUp(); y.RoundUp(); return *this;}          ///< Round all corners  upward (towards larger integers)
    Block & RoundDown() noexcept   {x.RoundDown(); y.RoundDown(); return *this;}      ///< Round all corners downward (towards smaller integers)
    Block & RoundWider() noexcept  {x.RoundWider(); y.RoundWider(); return *this;}    ///< Round corners outward.
    Block & RoundCloser() noexcept {x.RoundCloser(); y.RoundCloser(); return *this;}  ///< Round corners inward.

    Block &Rotate90CW() noexcept { if (IsInvalid()) return *this; const Range t = -x; x = y; y = t; return *this; }
    Block &Rotate90CCW() noexcept { if (IsInvalid()) return *this; const  Range t = -y; y = x; x = t; return *this; }
    Block &Rotate90AroundCentroid() noexcept { const XY C = Centroid(); SwapXY(); Shift(C-Centroid()); return *this; }

    double Distance(const XY &xy) const noexcept;
    double Distance(const Block &b) const  noexcept;
    Range Cut(const XY &A, const XY &B) const;
    std::optional<std::array<XY, 2>> CutEx(const XY &A, const XY &B) const;
    EContourRelationType RelationTo(const Block &c) const noexcept {
        if (IsInvalid()) return c.IsInvalid() ? REL_BOTH_EMPTY : REL_A_IS_EMPTY;
        if (c.IsInvalid()) return REL_B_IS_EMPTY;
        const EContourRelationType rx = x.RelationTo(c.x), ry = y.RelationTo(c.y);
        if (rx == ry) return rx;
        if (rx == REL_APART || ry == REL_APART) return REL_APART;
        return REL_OVERLAP;
    } ///> Returns the relation of the two Blocks.
    constexpr const Range &operator[](unsigned i) const noexcept { return i ? y : x; }
    constexpr const Range &operator[](bool i) const noexcept { return i ? y : x; }
    constexpr Range& operator[](unsigned i) noexcept { return i ? y : x; }
    constexpr Range& operator[](bool i) noexcept { return i ? y : x; }
    std::string Dump(bool precise) const;
};

/** Returns the distance of `xy` from the Block.
 *
 * This is calculated as the smallest distance between `xy` and any point on the Block edge.
 * If the point is inside, a negative value will result.
 */
inline double Block::Distance(const XY &xy) const noexcept
{
    const double xd = x.Distance(xy.x);
    const double yd = y.Distance(xy.y);
    //if both outside we return the distance
    if (fsign(xd)>0 && fsign(yd)>0) return sqrt(xd*xd + yd*yd);
    //if both inside, we are fully inside the box, return the smaller abs value
    if (fsign(xd)<0 && fsign(yd)<0) return std::max(xd, yd);
    //else we return the one with bigger sign (positive or zero)
    return fsign(xd) > fsign(yd) ? xd : yd;
}

/** Returns the distance of Block `b` from the Block.
 *
 * This is calculated as the smallest distance between any point on `B`'s edge
 * and any point on the Block edge.
 * If the two block have crossing edges, the distance is zero.
 * If one block is fully inside the other, a negative value will result.
 */
inline double Block::Distance(const Block &b) const noexcept
{
    const double uld = Distance(b.UpperLeft());
    const double urd = Distance(b.UpperRight());
    const double lld = Distance(b.LowerLeft());
    const double lrd = Distance(b.LowerRight());
    //if some squares of b are inside and some are outside (or there is a touch), return 0
    if (fsign(uld) != fsign(lrd) || fsign(uld) != fsign(urd) || fsign(uld) != fsign(lld))
        return 0;
    //if all are either inside or outside, return the one with smallest abs value
    return minabs(minabs(uld, urd), minabs(lld, lrd));
}


/** Generic 2D Affine transformation matrix
 * The Transformation will be:
 * x' = y*ab.x + x*a1.x + a0.x;
 * y' = y*a1.y + x*ab.y + a0.y;
 *
 * The actual affine transformation matrix looks like
 * | a1.x ab.x a0.x |
 * | ab.y a1.y a0.y |
 * |   0    0    1  |
 */
struct TRMatrix
{
    XY ab;
    XY a1;
    XY a0;
    TRMatrix() = default; ///<Uninitialized matrix
    constexpr TRMatrix(const XY &_ab, const XY &_a1, const XY & _a0) : ab(_ab), a1(_a1), a0(_a0) {} ///<Fully initialized matrix
    constexpr TRMatrix(bool) : ab{0, 0}, a1{1, 1}, a0{0, 0} {} ///<Matrix initialized to the identity matrix
    /**Return an identity matrix*/
    static constexpr TRMatrix CreateIdentity() { return TRMatrix(XY(0,0), XY(1,1), XY(0, 0)); }
    /** Return a matrix rotating 'x' degrees around the origin.
     * Rotation is clockwise (in a space, where y grows downwards).*/
    static constexpr TRMatrix CreateRotate(double cos, double sin) { return TRMatrix(XY(-sin, sin), XY(cos, cos), XY(0, 0)); }
    /** Return a matrix rotating 'degree' degrees around the origin.
     * Rotation is clockwise (in a space, where y grows downwards).*/
    static TRMatrix CreateRotate(double degree) { return CreateRotate(cos(degree*M_PI/180), sin(degree*M_PI/180)); } //cannot be constexpr due to sin/cos having side effects (errno)
    /** Return a matrix for translation.*/
    static constexpr TRMatrix CreateShift(const XY &shift) { return TRMatrix(XY(0,0), XY(1,1), shift); }
    /** Returns a matrix for linear scaling, separate amounts in the two directions.*/
    static constexpr TRMatrix CreateScale(const XY &scale) { return TRMatrix(XY(0,0), scale, XY(0,0)); }
    /** Returns a matrix for linear scaling.*/
    static constexpr TRMatrix CreateScale(double scale) { return TRMatrix(XY(0, 0), XY(scale, scale), XY(0, 0)); }
    /** Returns a matrix for mirroring to the Y axis.
     * Alternatively, if 'x' is nonzero, mirroring to a line parallel to the Y axis, but at 'x'.*/
    static constexpr TRMatrix CreateFlipYAxis(double x=0) { return TRMatrix(XY(0,0), XY(-1,1), XY(2*x, 0)); }
    /** Returns a matrix for mirroring to the X axis.
     * Alternatively, if 'y' is nonzero, mirroring to a line parallel to the X axis, but at 'y'.*/
    static constexpr TRMatrix CreateFlipXAxis(double y=0) { return TRMatrix(XY(0,0), XY(1,-1), XY(0, 2*y));  }
    /** True if this matrix is the identity matrix.*/
    constexpr bool IsIdentity() const { return ab.x==0 && ab.y==0 && a1.x==1 && a1.y==1 && a0.x==0 && a0.y==0; }
    /** True if this matrix is the null matrix.*/
    constexpr bool IsNull() const { return ab.x==0 && ab.y==0 && a1.x==0 && a1.y==0; }
    /** True if this matrix is degenerate, that is it discards input x or y value.*/
    constexpr bool IsDegenerate() const { return (ab.x==0 && a1.x==0) || (ab.y==0 && a1.y==0); }
    /** True if this matrix represents a transformation, that changes clockwiseness of closed contours.*/
    constexpr bool IsFlipping() const;
    /** Multiply the two matrices, effectively resulting in applying the two transformations in sequence. */
    constexpr TRMatrix operator *(const TRMatrix &o) const;
    /** Invert the matrix: the result will undo us. If is UB if the matrix cannot be inverted.*/
    constexpr TRMatrix Invert() const;

    /** Some 2D affine transformations can be decomposed to a combination of 
     * shift, scale and rotate (in this order). scale can be negative.*/
    struct Decomposed {
        XY shift;
        XY scale;
        XY rotate; //x:cos, y:sin
    };
    /** Decompose the matrix to a rotation, scaling and shift component, if possible. Returns empty if the transformation contains sheering or negative scaling.*/
    std::optional<Decomposed> Decompose() const;
};

/** Applies the transformation 'M' to 'xy'.*/
constexpr XY operator *(const XY&xy, const TRMatrix &M) noexcept { return{xy.y*M.ab.x + xy.x*M.a1.x + M.a0.x, xy.y*M.a1.y + xy.x*M.ab.y + M.a0.y}; }
/** Applies the transformation 'M' to 'xy'.*/
constexpr XY operator *(const TRMatrix &M, const XY&xy) noexcept { return{xy.y*M.ab.x + xy.x*M.a1.x + M.a0.x, xy.y*M.a1.y + xy.x*M.ab.y + M.a0.y}; }
/** Applies the transformation 'M' to us.*/
constexpr XY &XY::Apply(const TRMatrix &M) noexcept { return *this = *this*M; }

#ifdef WIN32
#define sprintf sprintf_s
#endif

/** Creates a C++ definition of this object. */
inline std::string XY::Dump(bool precise) const
{
    char tmp[200];
    snprintf(tmp, sizeof(tmp), precise ? "XY(%a,%a)" : "XY(%g,%g)", x, y);
    return tmp;
}

inline std::string Block::Dump(bool precise) const {
    return "Block("+UpperLeft().Dump(precise)+","+LowerRight().Dump(precise)+")";
}

constexpr bool TRMatrix::IsFlipping() const
{
    //take clockwise triangle (0,0)->(0,1)->(1,0) and transform it (skipping shift)
    //We get (0,0)->(ab.x, a1.y)->(a1.x, ab.y). If it is counterclockwise the transformation is flipping
    //Sum over the edges (x2-x1)*(y2+y1). If the sum is negative the triangle is counterclockwise
    return 0 > ((ab.x-0)*(a1.y+0) + (a1.x-ab.x)*(ab.y+a1.y) + (0-a1.x)*(0-ab.y));
}

/** Constexpr matrix multiplication. Watch out! If you want
 * transformation 'B' to come *after* transformation 'A',
 * you will need B*A and not vice versa!*/
constexpr TRMatrix TRMatrix::operator *(const TRMatrix &o) const
{
    return TRMatrix(XY(a1.x*o.ab.x + ab.x*o.a1.y, ab.y*o.a1.x + a1.y*o.ab.y),
                    XY(a1.x*o.a1.x + ab.x*o.ab.y, ab.y*o.ab.x + a1.y*o.a1.y),
                    XY(a1.x*o.a0.x + ab.x*o.a0.y + a0.x*1, ab.y*o.a0.x + a1.y*o.a0.y + a0.y*1));
}

constexpr TRMatrix TRMatrix::Invert() const {
    const double det = a1.x * a1.y - ab.x * ab.y;
    return TRMatrix{ //https://en.wikipedia.org/wiki/Adjugate_matrix
        -ab/det,
        XY{a1.y, a1.x}/det,
        XY{ab.x * a0.y - a0.x * a1.y, -a1.x * a0.y + a0.x * ab.y}/det
    };
}

inline std::optional<TRMatrix::Decomposed> TRMatrix::Decompose() const {
    std::optional<TRMatrix::Decomposed> ret(std::in_place);
    ret->shift = a0;
    ret->scale = { sqrt(a1.x * a1.x + ab.x * ab.x), sqrt(a1.y * a1.y + ab.y * ab.y) };
    double cos1 = a1.y / ret->scale.y;
    double cos2 = a1.x / ret->scale.x;
    double sin1 = -ab.x / ret->scale.x;
    double sin2 = ab.y / ret->scale.y;
    //if this transformation uses only rotation, positive scaling and shifting, the above values should match.
    //we can cater for negative scaling, so allow sign differences
    if (test_equal(fabs(cos1), fabs(cos2)) && test_equal(fabs(sin1), fabs(sin2)) && test_equal(fabs(sin1 * sin2) + fabs(cos1 * cos2), 1))
        //try determining the sign of the scale
        for (XY s : {XY{ 1,1 }, XY{ -1,1 }, XY{ 1,-1 }, XY{ -1,-1 }})
            if (test_equal(s.x * cos1, s.y * cos2) && test_equal(s.x * sin1, s.y * sin2)) {
                ret->rotate = { s.x * cos1, s.x * sin1 };
                ret->scale.Scale(s);
                return ret;
            }
    ret.reset();
    return ret;
}

/** Returns the cosine of how many degrees shall we rotate the x axis clockwise so that it crosses 'point'.
* All this is in a space where y grows downwards.*/
inline double GetDegreeCos(const XY &origin, const XY &point)
{
    return (point.x-origin.x)/origin.Distance(point);
}

/** Returns the cosine of how many degrees shall we rotate the x axis clockwise so that it crosses 'point'.
* All this is in a space where y grows downwards.*/
inline double GetDegreeSin(const XY &origin, const XY &point)
{
    return (point.y-origin.y)/origin.Distance(point);
}

/** Returns how many degrees shall we rotate the x axis clockwise so that it crosses 'point'.
* All this is in a space where y grows downwards.*/
inline double GetDegree(const XY &origin, const XY &point)
{
    const double rad = acos(GetDegreeCos(origin, point));
    return (point.y>origin.y ? rad : 2*M_PI-rad) * 180/M_PI;
}

} //namespace

#endif //CONTOUR_BASICS_H
