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
// Copyright (c) 2018-2020 Thomas Kramer.
// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! `Text` is used as labels associated with a point.

use crate::point::Point;
use crate::traits::*;
use std::ops::Deref;
use crate::rect::Rect;
use num_traits::NumCast;
use std::fmt;
use std::hash::Hash;

/// Trait for types that can be used as the text of this label.
/// The most simple solution is to use `String`. However, in many cases
/// where the same text is used in many labels it might be desirable to use 'string interning'
/// for more efficient memory usage. Then an `Rc<String>` could be used for instance.
pub trait TextType: Eq + Hash + Clone + fmt::Debug {}

impl<T: Eq + Hash + Clone + fmt::Debug> TextType for T {}

/// A text is a point associated with a string.
/// This struct does not define how the text should be rendered on screen.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Text<T, S = String> {
    /// Location of the label.
    location: Point<T>,
    /// Text content.
    text: S,
}

/// Display format of the text label.
impl<T, S> fmt::Display for Text<T, S>
    where T: fmt::Display,
          S: TextType + fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Text({}, {})", self.text, self.location)
    }
}

impl<T, S> Deref for Text<T, S>
    where S: Deref<Target=String> {
    type Target = String;

    /// Dereference to String.
    fn deref(&self) -> &Self::Target {
        self.text.deref()
    }
}

impl<T, S: TextType> Text<T, S> {
    /// Create a new text object.
    pub fn new(text: S, location: Point<T>) -> Self {
        Text {
            location,
            text,
        }
    }

    /// Get a reference to the text string.
    pub fn text(&self) -> &S {
        &self.text
    }
}

impl<T: Copy, S: TextType> Text<T, S> {
    /// Get location of the text label.
    #[inline]
    pub fn location(&self) -> Point<T> {
        self.location
    }

    /// Get x-coordinate of the label location.
    #[inline]
    pub fn x(&self) -> T {
        self.location.x
    }

    /// Get y-coordinate of the label location.
    #[inline]
    pub fn y(&self) -> T {
        self.location.y
    }
}


impl<T: Copy + PartialOrd, S> TryBoundingBox<T> for Text<T, S> {
    fn try_bounding_box(&self) -> Option<Rect<T>> {
        Some(
            Rect::new(self.location, self.location)
        )
    }
}


impl<T, Dst, S> TryCastCoord<T, Dst> for Text<T, S>
    where T: Copy + NumCast,
          Dst: Copy + NumCast,
          S: Clone {
    type Output = Text<Dst, S>;

    fn try_cast(&self) -> Option<Self::Output> {
        self.location.try_cast()
            .map(|loc| Text {
                location: loc,
                text: self.text.clone(),
            })
    }
}

/// Point wise transformation for a single point.
impl<T, S> MapPointwise<T> for Text<T, S>
    where T: Copy,
          S: Clone
{
    /// Point wise transformation.
    #[inline]
    fn transform<F>(&self, transformation: F) -> Self
        where F: Fn(Point<T>) -> Point<T> {
        Text {
            location: transformation(self.location),
            text: self.text.clone(),
        }
    }
}