File indexing completed on 2024-05-12 03:47:45
0001 /* 0002 File : Interval.h 0003 Project : LabPlot 0004 Description : Auxiliary class for interval based data 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2007 Tilman Benkert <thzs@gmx.net> 0007 SPDX-FileCopyrightText: 2007 Knut Franke <knut.franke@gmx.de> 0008 SPDX-FileCopyrightText: 2012 Alexander Semke <alexander.semke@web.de> 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #ifndef INTERVAL_H 0013 #define INTERVAL_H 0014 0015 #include "backend/nsl/nsl_math.h" 0016 #include <QVector> 0017 0018 template<class T> 0019 class Interval; 0020 0021 template<class T> 0022 class IntervalBase { 0023 public: 0024 IntervalBase() 0025 : m_start(-1) 0026 , m_end(-1) { 0027 } 0028 IntervalBase(T start, T end) { 0029 m_start = start; 0030 m_end = end; 0031 } 0032 IntervalBase(IntervalBase const&) = default; 0033 virtual ~IntervalBase() = default; 0034 IntervalBase& operator=(const IntervalBase&) = default; 0035 T start() const { 0036 return m_start; 0037 } 0038 T end() const { 0039 return m_end; 0040 } 0041 void setStart(T start) { 0042 m_start = start; 0043 } 0044 void setEnd(T end) { 0045 m_end = end; 0046 } 0047 bool contains(const Interval<T>& other) const { 0048 return (m_start <= other.start() && m_end >= other.end()); 0049 } 0050 bool contains(T value) const { 0051 return (m_start <= value && m_end >= value); 0052 } 0053 bool fuzzyContains(T value) const { 0054 bool rc1 = nsl_math_definitely_less_than(m_start, value); 0055 bool rc2 = nsl_math_definitely_greater_than(m_end, value); 0056 return (rc1 && rc2); 0057 } 0058 bool intersects(const Interval<T>& other) const { 0059 return (contains(other.start()) || contains(other.end())); 0060 } 0061 //! Return the intersection of two intervals 0062 /** 0063 * This function returns an invalid interval if the two intervals do not intersect. 0064 */ 0065 static Interval<T> intersection(const Interval<T>& first, const Interval<T>& second) { 0066 return Interval<T>(qMax(first.start(), second.start()), qMin(first.end(), second.end())); 0067 } 0068 void translate(T offset) { 0069 m_start += offset; 0070 m_end += offset; 0071 } 0072 bool operator==(const Interval<T>& other) const { 0073 return (m_start == other.start() && m_end == other.end()); 0074 } 0075 Interval<T>& operator=(const Interval<T>& other) { 0076 m_start = other.start(); 0077 m_end = other.end(); 0078 return *this; 0079 } 0080 //! Returns true if no gap is between two intervals. 0081 virtual bool touches(const Interval<T>& other) const = 0; 0082 //! Merge two intervals that touch or intersect 0083 static Interval<T> merge(const Interval<T>& a, const Interval<T>& b) { 0084 if (!(a.intersects(b) || a.touches(b))) 0085 return a; 0086 return Interval<T>(qMin(a.start(), b.start()), qMax(a.end(), b.end())); 0087 } 0088 //! Subtract an interval from another 0089 static QVector<Interval<T>> subtract(const Interval<T>& src_iv, const Interval<T>& minus_iv) { 0090 QVector<Interval<T>> list; 0091 if ((src_iv == minus_iv) || (minus_iv.contains(src_iv))) 0092 return list; 0093 0094 if (!src_iv.intersects(minus_iv)) 0095 list.append(src_iv); 0096 else if (src_iv.end() <= minus_iv.end()) 0097 list.append(Interval<T>(src_iv.start(), minus_iv.start() - 1)); 0098 else if (src_iv.start() >= minus_iv.start()) 0099 list.append(Interval<T>(minus_iv.end() + 1, src_iv.end())); 0100 else { 0101 list.append(Interval<T>(src_iv.start(), minus_iv.start() - 1)); 0102 list.append(Interval<T>(minus_iv.end() + 1, src_iv.end())); 0103 } 0104 0105 return list; 0106 } 0107 //! Split an interval into two 0108 static QVector<Interval<T>> split(const Interval<T>& i, T before) { 0109 QVector<Interval<T>> list; 0110 if (before < i.start() || before > i.end()) { 0111 list.append(i); 0112 } else { 0113 Interval<T> left(i.start(), before - 1); 0114 Interval<T> right(before, i.end()); 0115 if (left.isValid()) 0116 list.append(left); 0117 if (right.isValid()) 0118 list.append(right); 0119 } 0120 return list; 0121 } 0122 //! Merge an interval into a list 0123 /* 0124 * This function merges all intervals in the list until none of them 0125 * intersect or touch anymore. 0126 */ 0127 static void mergeIntervalIntoList(QVector<Interval<T>>* list, Interval<T> i) { 0128 for (int c = 0; c < list->size(); c++) { 0129 if (list->at(c).touches(i) || list->at(c).intersects(i)) { 0130 Interval<T> result = merge(list->takeAt(c), i); 0131 mergeIntervalIntoList(list, result); 0132 return; 0133 } 0134 } 0135 list->append(i); 0136 } 0137 //! Restrict all intervals in the list to their intersection with a given interval 0138 /** 0139 * Remark: This may decrease the list size. 0140 */ 0141 static void restrictList(QVector<Interval<T>>* list, Interval<T> i) { 0142 Interval<T> temp; 0143 for (int c = 0; c < list->size(); c++) { 0144 temp = intersection(list->at(c), i); 0145 if (!temp.isValid()) 0146 list->removeAt(c--); 0147 else 0148 list->replace(c, temp); 0149 } 0150 } 0151 //! Subtract an interval from all intervals in the list 0152 /** 0153 * Remark: This may increase or decrease the list size. 0154 */ 0155 static void subtractIntervalFromList(QVector<Interval<T>>* list, Interval<T> i) { 0156 QVector<Interval<T>> temp_list; 0157 for (int c = 0; c < list->size(); c++) { 0158 temp_list = subtract(list->at(c), i); 0159 if (temp_list.isEmpty()) 0160 list->removeAt(c--); 0161 else { 0162 list->replace(c, temp_list.at(0)); 0163 if (temp_list.size() > 1) 0164 list->insert(c, temp_list.at(1)); 0165 } 0166 } 0167 } 0168 QVector<Interval<T>> operator-(QVector<Interval<T>> subtrahend) { 0169 QVector<Interval<T>>*tmp1, *tmp2; 0170 tmp1 = new QVector<Interval<T>>(); 0171 *tmp1 << *static_cast<Interval<T>*>(this); 0172 for (const auto& i : subtrahend) { 0173 tmp2 = new QVector<Interval<T>>(); 0174 for (const auto& j : *tmp1) 0175 *tmp2 << subtract(j, i); 0176 delete tmp1; 0177 tmp1 = tmp2; 0178 } 0179 QVector<Interval<T>> result = *tmp1; 0180 delete tmp1; 0181 return result; 0182 } 0183 0184 //! Return a string in the format '[start,end]' 0185 QString toString() const { 0186 return "[" + QString::number(m_start) + "," + QString::number(m_end) + "]"; 0187 } 0188 0189 protected: 0190 //! Interval start 0191 T m_start; 0192 //! Interval end 0193 T m_end; 0194 }; 0195 0196 //! Auxiliary class for interval based data 0197 /** 0198 * This class represents a data range of 0199 * the type [start,end] where start > end is possible. 0200 * 0201 * For the template argument (T), only numerical types ((unsigned) short, (unsigned) int, 0202 * (unsigned) long, float, double, long double) are supported. 0203 */ 0204 template<class T> 0205 class Interval : public IntervalBase<T> { 0206 public: 0207 Interval() = default; 0208 Interval(T start, T end) 0209 : IntervalBase<T>(start, end) { 0210 } 0211 T size() const { // why "+1"? 0212 return IntervalBase<T>::m_end - IntervalBase<T>::m_start + 1; 0213 } 0214 bool isValid() const { // why >=0 ? 0215 return (IntervalBase<T>::m_start >= 0 && IntervalBase<T>::m_end >= 0 && IntervalBase<T>::m_start <= IntervalBase<T>::m_end); 0216 } 0217 bool touches(const Interval<T>& other) const override { // why "+/- 1" ? 0218 return ((other.end() == IntervalBase<T>::m_start - 1) || (other.start() == IntervalBase<T>::m_end + 1)); 0219 } 0220 }; 0221 0222 #endif