15.1.150. tablet_qt/graphics/geometry.h¶

```/*
Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

This file is part of CamCOPS.

CamCOPS is free software: you can redistribute it and/or modify
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

CamCOPS 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 CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once
#include <QPointF>
#include <QtGlobal>
#include <QWidget>  // for QWIDGETSIZE_MAX
#include "graphics/linesegment.h"

namespace geometry
{

/*

Standard Cartesian/polar coordinate systems
===============================================================================

- Positive x is to the right.
- Positive y is UP.
- Positive theta is ANTICLOCKWISE, and theta = 0 is at the 3 o'clock position.
... so for a point (x=1, y=0), positive rotation moves it in the direction of
INCREASING y.
When you rotate by theta, you rotate anticlockwise.
https://en.wikipedia.org/wiki/Rotation_of_axes

The Qt coordinate system
===============================================================================

- Positive x is to the right.
- Positive y is DOWN. (This matches commonplace screen coordinates; the origin
is at the top left.)
https://doc.qt.io/qt-6.5/coordsys.html

- When you rotate a coordinate system, rotation angles are CLOCKWISE.
https://doc.qt.io/qt-6.5/qpainter.html#rotate
... so for a point (x=1, y=0), positive rotation moves it in the direction of
INCREASING y.

- But when you draw a pie, rotation angles are ANTICLOCKWISE, and zero degrees
is in the 3 o'clock position.
https://doc.qt.io/qt-6.5/qpainter.html#drawPie

- Other ANTICLOCKWISE bits:
- QTranform::rotate
https://doc.qt.io/qt-6.5/qtransform.html#rotate

- Qt also uses a "positive ANTICLOCKWISE" system for its graphs, though that's
more obvious as it's mimicking standard graph geometry here.
https://doc.qt.io/qt-6.5/qtcharts-polarchart-example.html

Which representation to use internally for polar coordinates?
===============================================================================

- Any sophisticated representations are going to assume a standard Cartesian
system and the most important part of that isn't "up"/"down" but the fact
that positive angles are anticlockwise WITH RESPECT TO "x right, y up", i.e.
that positive rotation moves the point (x=1, y=0) in the direction of
INCREASING y.

That's helpful so we can use standard representations like
x = r * cos(theta)      y = r * sin(theta)
not
x = r * cos(theta)      y = -r * sin(theta)

- That means angles are clockwise in the standard Qt coordinates.

- So we'll use that when we refer to "polar", and convert explicitly for those
places (like pie drawing) where anticlockwise angles are required.

===============================================================================

- These are based on the idea of "North up" (though also support a
transformation via an "alternative North"), and positive rotation is
CLOCKWISE.

Other notes on Qt coordinates
===============================================================================

- QPainter::drawText()

"The y-position is used as the baseline of the font."

0123456789
0   |
1   SSOOMMEE  TTEEXXTT
2   SSOOMMEE  TTEEXXTT
3 - SSOOMMEE  TTEEXXTT -      [descenders go below line?]
4   |

So if you draw at y = 3, it'll be bottom-aligned there.
To top-align it, add its height to the y coordinate.
To vcentre-align it, add half its height to the y coordinate.

To left-align it, plot at the unmodified x coordinate.
To centre-align it, subtract half its width from the x coordinate.
To right-align it, subtract its width from the x coordinate.

*/

extern const qreal DEG_0;
extern const qreal DEG_90;
extern const qreal DEG_270;
extern const qreal DEG_180;
extern const qreal DEG_360;

// Converts degrees to sixteenths of a degree.

// Converts clockwise to anticlockwise degrees (!).
inline qreal clockwiseToAnticlockwise(qreal degrees) { return -degrees; }

// Converts anticlockwise to clockwise degrees (!).
inline qreal anticlockwiseToClockwise(qreal degrees) { return -degrees; }

// Returns a heading normalized to [0, 360).

// Are the two headings fuzzy-equal?

// Is heading_deg in the range (first_bound_deg, second_bound_deg)?
// Or, if inclusive is true, in [first_bound_deg, second_bound_deg]?
qreal second_bound_deg,
bool inclusive = false);

// Converts a compass heading from a "true" to a "pseudo" system, based on
// pseudo_north_deg.
qreal pseudo_north_deg,
bool normalize = true);

// Converts a compass heading from a "pseudo" to a "true" system, based on
// pseudo_north_deg.
qreal pseudo_north_deg,
bool normalize = true);

// Returns a point (relative to the origin) equivalent to the specified
// polar coordinates.
QPointF polarToCartesian(qreal r, qreal theta_degrees);

// Returns the distance between two points.
qreal distanceBetween(const QPointF& from, const QPointF& to);

// Returns the heading (in polar degrees, 0 = along x axis), "from" -> "to".
qreal polarThetaDeg(const QPointF& from, const QPointF& to);

// Returns the heading (in polar degrees, 0 = along x axis), origin -> "to".

// Converts a polar angle to a compass heading.
qreal polarThetaToHeading(qreal theta_deg, qreal north_deg = 0.0);

// Converts a compass heading to a polar angle.
bool normalize = true);

// Returns a compass heading, "from" -> "to".
qreal headingDegrees(const QPointF& from, const QPointF& to,
qreal north_deg = 0.0);

// Return a line segment starting at "point", travelling in compass direction
// "heading" (where north_deg indicates our North direction), with line length
qreal north_deg = 0.0,

// (1) Draw a line from "from" to "to".
// (2) Draw a line from "point" in direction "heading", where increasing
//     values of "heading" are clockwise, and a heading of 0 degrees is
//     the North direction (where that is defined by north_deg degrees
//     clockwise of "screen up").
// (3) Do the two lines cross?
qreal north_deg = 0.0,

// Does the line "from" -> "to" pass below "point"?
bool linePassesBelowPoint(const QPointF& from, const QPointF& to,
const QPointF& point);

}  // namespace geometry
```