1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
// Copyright (c) 2018-2020 Thomas Kramer.
// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
//
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Commonly used type definitions and constants.
use std::ops::{Add, Neg, Sub};
/// Precision for distance related decisions.
pub const PREC_DISTANCE: DistanceType = 1e-5;
/// Default floating point type.
pub type FloatType = f64;
/// Default type for euclidean distances.
pub type DistanceType = FloatType;
/// Angle expressed as a multiple of 90 degrees.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Angle {
/// 0 Degrees.
#[default]
R0,
/// 90 Degrees.
R90,
/// 180 Degrees.
R180,
/// 270 Degrees.
R270,
}
impl Angle {
/// Describe the angle as a integer multiple of 90 degrees.
pub fn as_int(&self) -> u32 {
match self {
Angle::R0 => 0,
Angle::R90 => 1,
Angle::R180 => 2,
Angle::R270 => 3,
}
}
/// Convert an integer to an angle.
/// The integer specifies the number of 90 degree rotations.
pub fn from_u32(a: u32) -> Self {
match a % 4 {
0 => Angle::R0,
1 => Angle::R90,
2 => Angle::R180,
3 => Angle::R270,
_ => unreachable!(),
}
}
}
impl Add for Angle {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::from_u32(self.as_int() + rhs.as_int())
}
}
impl Sub for Angle {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::from_u32(self.as_int() + 4 - rhs.as_int())
}
}
impl Neg for Angle {
type Output = Self;
fn neg(self) -> Self::Output {
Self::from_u32(4 - self.as_int())
}
}
/// Location relative to a directed line or edge.
/// Something can be on the left of the line, right of the line or on top of the line (center).
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub enum Side {
/// Location on the left side.
Left,
/// Neither on the left nor right, but on top.
Center,
/// Location on the right side.
Right,
}
impl Side {
/// Test if this is the left side.
pub fn is_left(&self) -> bool {
*self == Side::Left
}
/// Test if this is the right side.
pub fn is_right(&self) -> bool {
*self == Side::Right
}
/// Test if this is `Center`.
pub fn is_center(&self) -> bool {
*self == Side::Center
}
/// Get the other side.
pub fn other(&self) -> Self {
match self {
Side::Left => Side::Right,
Side::Center => Side::Center,
Side::Right => Side::Left,
}
}
}
/// Relative orientation of two geometrical objects such as vectors.
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub enum Orientation {
/// Clock-wise orientation.
ClockWise,
/// Counter-clock-wise orientation.
CounterClockWise,
/// Neither clock-wise nor counter-clock-wise.
Straight,
}
impl Orientation {
/// Test if the orientation is equal to `ClockWise`.
pub fn is_clock_wise(self) -> bool {
self == Self::ClockWise
}
/// Test if the orientation is equal to `CounterClockWise`.
pub fn is_counter_clock_wise(self) -> bool {
self == Self::CounterClockWise
}
/// Test if the orientation is equal to `Straight`.
pub fn is_straight(self) -> bool {
self == Self::Straight
}
}
/// This is a result type for containment checks.
/// * `No` Not inside.
/// * `OnBounds` Lies on the boundaries.
/// * `WithinBounds` Fully inside.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ContainsResult {
/// Does not contain the point.
No,
/// Contains the point but on the borders/end-points.
OnBounds,
/// Fully contains the point.
WithinBounds,
}
impl ContainsResult {
/// Tells if the point is contained but does not lie on the bounds.
pub fn is_within_bounds(&self) -> bool {
matches!(self, ContainsResult::WithinBounds)
}
/// Tells if the point is contained or lies on the bounds.
pub fn inclusive_bounds(&self) -> bool {
matches!(
self,
ContainsResult::WithinBounds | ContainsResult::OnBounds
)
}
/// Check if the point neither is on the bounds nor within the bounds.
pub fn on_bounds(&self) -> bool {
matches!(self, ContainsResult::OnBounds)
}
/// Check if the point lies on the bounds.
pub fn is_no(&self) -> bool {
matches!(self, ContainsResult::No)
}
/// Returns the stronger result of the both.
/// Ordering from weak to strong is `No`, `OnBounds`, `WithinBounds`
pub fn max(self, other: Self) -> Self {
match (self, other) {
(ContainsResult::WithinBounds, _) => ContainsResult::WithinBounds,
(_, ContainsResult::WithinBounds) => ContainsResult::WithinBounds,
(ContainsResult::OnBounds, _) => ContainsResult::OnBounds,
(_, ContainsResult::OnBounds) => ContainsResult::OnBounds,
(ContainsResult::No, _) => ContainsResult::No,
}
}
/// Returns the weaker result of the both.
/// Ordering from weak to strong is `No`, `OnBounds`, `WithinBounds`
pub fn min(self, other: Self) -> Self {
match (self, other) {
(ContainsResult::No, _) => ContainsResult::No,
(_, ContainsResult::No) => ContainsResult::No,
(ContainsResult::OnBounds, _) => ContainsResult::OnBounds,
(_, ContainsResult::OnBounds) => ContainsResult::OnBounds,
(ContainsResult::WithinBounds, _) => ContainsResult::WithinBounds,
}
}
}