985 lines
44 KiB
C++
985 lines
44 KiB
C++
//! \addtogroup Containers
|
||
//! \{
|
||
//! \file pivector2d.h
|
||
//! \brief
|
||
//! \~english Declares \a PIVector2D
|
||
//! \~russian Объявление \a PIVector2D
|
||
//! \~\authors
|
||
//! \~english
|
||
//! Andrey Bychkov work.a.b@yandex.ru;
|
||
//! \~russian
|
||
//! Андрей Бычков work.a.b@yandex.ru;
|
||
//! \~\}
|
||
/*
|
||
PIP - Platform Independent Primitives
|
||
2D wrapper around PIVector
|
||
Andrey Bychkov work.a.b@yandex.ru
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU Lesser General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program 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 Lesser General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Lesser General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#ifndef PIVECTOR2D_H
|
||
#define PIVECTOR2D_H
|
||
|
||
#include "pipair.h"
|
||
#include "pivector.h"
|
||
|
||
//! \addtogroup Containers
|
||
//! \{
|
||
//! \class PIVector2D
|
||
//! \brief
|
||
//! \~english 2D array container.
|
||
//! \~russian Двумерный контейнер-массив.
|
||
//! \details
|
||
//! \~english
|
||
//! This class is used to store a 2D array of elements of any type as a single continuous block of memory (a plain PIVector).
|
||
//! Elements can be accessed using the `[][]` operators, where the first index is the row and the second is the column.
|
||
//! Rows can be manipulated as \a PIVector objects, allowing modification of individual elements or assignment of entire rows.
|
||
//! You cannot directly add or remove elements to change the dimensions of the array after construction
|
||
//! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements.
|
||
//! \~russian
|
||
//! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного
|
||
//! PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со
|
||
//! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя
|
||
//! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a
|
||
//! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов.
|
||
|
||
|
||
template<typename T>
|
||
class PIVector2D {
|
||
public:
|
||
//! \~english Constructs an empty 2D array.
|
||
//! \~russian Создает пустой двумерный массив.
|
||
inline PIVector2D() { rows_ = cols_ = 0; }
|
||
|
||
//! \~english Constructs a 2D array with the given dimensions, filled with copies of `f`.
|
||
//! \~russian Создает двумерный массив заданного размера, заполненный копиями `f`.
|
||
//! \param rows Number of rows.
|
||
//! \param cols Number of columns.
|
||
//! \param f Value to fill the array with.
|
||
//! \~english \param rows Количество строк.
|
||
//! \~russian \param rows Количество строк.
|
||
//! \~english \param cols Количество столбцов.
|
||
//! \~russian \param cols Количество столбцов.
|
||
//! \~english \param f Значение для заполнения массива.
|
||
//! \~russian \param f Значение для заполнения массива.
|
||
inline PIVector2D(size_t rows, size_t cols, const T & f = T()) {
|
||
rows_ = rows;
|
||
cols_ = cols;
|
||
mat.resize(rows * cols, f);
|
||
}
|
||
|
||
//! \~english Constructs a 2D array from an existing 1D vector, reshaping it.
|
||
//! \~russian Создает двумерный массив из существующего одномерного вектора, изменяя его форму.
|
||
//! \param rows Number of rows.
|
||
//! \param cols Number of columns.
|
||
//! \param v The source 1D vector. Its size must be at least `rows * cols`.
|
||
inline PIVector2D(size_t rows, size_t cols, const PIVector<T> & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); }
|
||
|
||
//! \~english Move constructs a 2D array from an existing 1D vector, reshaping it.
|
||
//! \~russian Конструктор перемещения из существующего одномерного вектора, изменяя его форму.
|
||
//! \param rows Number of rows.
|
||
//! \param cols Number of columns.
|
||
//! \param v The source 1D vector (rvalue reference). Its size must be at least `rows * cols`.
|
||
inline PIVector2D(size_t rows, size_t cols, PIVector<T> && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); }
|
||
|
||
//! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size.
|
||
//! \~russian Создает двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют
|
||
//! одинаковый размер. \param v The source vector of vectors.
|
||
inline PIVector2D(const PIVector<PIVector<T>> & v) {
|
||
rows_ = v.size();
|
||
if (rows_) {
|
||
cols_ = v[0].size();
|
||
mat.reserve(rows_ * cols_);
|
||
for (size_t i = 0; i < rows_; i++) {
|
||
mat.append(v[i]);
|
||
}
|
||
mat.resize(rows_ * cols_);
|
||
}
|
||
if (mat.isEmpty()) rows_ = cols_ = 0;
|
||
}
|
||
|
||
//! \~english Number of rows.
|
||
//! \~russian Количество строк.
|
||
inline size_t rows() const { return rows_; }
|
||
|
||
//! \~english Number of columns.
|
||
//! \~russian Количество столбцов.
|
||
inline size_t cols() const { return cols_; }
|
||
|
||
//! \~english Total number of elements (`rows * cols`).
|
||
//! \~russian Общее количество элементов (`строки * столбцы`).
|
||
inline size_t size() const { return mat.size(); }
|
||
|
||
//! \~english Total number of elements as signed value.
|
||
//! \~russian Общее количество элементов в виде знакового числа.
|
||
inline ssize_t size_s() const { return mat.size_s(); }
|
||
|
||
//! \~english Total number of elements.
|
||
//! \~russian Общее количество элементов.
|
||
inline size_t length() const { return mat.length(); }
|
||
|
||
//! \~english Number of elements that the underlying container has currently allocated space for.
|
||
//! \~russian Количество элементов, для которого сейчас выделена память во внутреннем контейнере.
|
||
inline size_t capacity() const { return mat.capacity(); }
|
||
|
||
//! \~english Checks if the array has no elements.
|
||
//! \~russian Проверяет, пуст ли массив.
|
||
inline bool isEmpty() const { return mat.isEmpty(); }
|
||
|
||
//! \~english Checks if the array has elements.
|
||
//! \~russian Проверяет, не пуст ли массив.
|
||
inline bool isNotEmpty() const { return mat.isNotEmpty(); }
|
||
|
||
|
||
//! \class Row
|
||
//! \brief
|
||
//! \~english Proxy class representing a single row in a \a PIVector2D for modification.
|
||
//! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации.
|
||
class Row {
|
||
friend class PIVector2D<T>;
|
||
|
||
private:
|
||
inline Row(PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
||
st_ = p->cols_ * row;
|
||
sz_ = p->cols_;
|
||
}
|
||
PIVector<T> * p_;
|
||
size_t st_, sz_;
|
||
|
||
public:
|
||
//! \~english Size of the row (number of columns).
|
||
//! \~russian Размер строки (количество столбцов).
|
||
inline size_t size() const { return sz_; }
|
||
|
||
//! \~english Accesses the element at the given column index within the row.
|
||
//! \~russian Доступ к элементу по заданному индексу столбца в строке.
|
||
inline T & operator[](size_t index) { return (*p_)[st_ + index]; }
|
||
|
||
//! \~english Const access to the element at the given column index within the row.
|
||
//! \~russian Константный доступ к элементу по заданному индексу столбца в строке.
|
||
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
||
|
||
//! \~english Returns a pointer to the row data starting at an optional offset.
|
||
//! \~russian Возвращает указатель на данные строки, начиная с опционального смещения.
|
||
inline T * data(size_t index = 0) { return p_->data(st_ + index); }
|
||
|
||
//! \~english Returns a const pointer to the row data starting at an optional offset.
|
||
//! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения.
|
||
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
||
|
||
//! \~english Assigns the contents of another Row to this row.
|
||
//! \~russian Присваивает этой строке содержимое другой строки.
|
||
inline Row & operator=(const Row & other) {
|
||
if (p_ == other.p_ && st_ == other.st_) return *this;
|
||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Assigns the contents of a \a PIVector to this row.
|
||
//! \~russian Присваивает этой строке содержимое \a PIVector.
|
||
inline Row & operator=(const PIVector<T> & other) {
|
||
const size_t sz = piMin<size_t>(sz_, other.size());
|
||
p_->_copyRaw(p_->data(st_), other.data(), sz);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Converts the row to a \a PIVector.
|
||
//! \~russian Преобразует строку в \a PIVector.
|
||
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
||
|
||
// --- Поиск в строке ---
|
||
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if ((*p_)[st_ + i] == e) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if ((*p_)[st_ + i] == e) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if (test((*p_)[st_ + i])) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if (test((*p_)[st_ + i])) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
};
|
||
|
||
//! \class Col
|
||
//! \brief
|
||
//! \~english Proxy class representing a single column in a \a PIVector2D for modification.
|
||
//! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации.
|
||
class Col {
|
||
friend class PIVector2D<T>;
|
||
|
||
private:
|
||
inline Col(PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||
step_ = p->cols_;
|
||
col_ = col;
|
||
sz_ = p->rows_;
|
||
}
|
||
PIVector<T> * p_;
|
||
size_t step_, col_, sz_;
|
||
|
||
public:
|
||
//! \~english Size of the column (number of rows).
|
||
//! \~russian Размер столбца (количество строк).
|
||
inline size_t size() const { return sz_; }
|
||
|
||
//! \~english Accesses the element at the given row index within the column.
|
||
//! \~russian Доступ к элементу по заданному индексу строки в столбце.
|
||
inline T & operator[](size_t index) { return (*p_)[index * step_ + col_]; }
|
||
|
||
//! \~english Const access to the element at the given row index within the column.
|
||
//! \~russian Константный доступ к элементу по заданному индексу строки в столбце.
|
||
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; }
|
||
|
||
//! \~english Returns a pointer to the column data starting at an optional row offset.
|
||
//! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам.
|
||
inline T * data(size_t index = 0) { return p_->data(index * step_ + col_); }
|
||
|
||
//! \~english Returns a const pointer to the column data starting at an optional row offset.
|
||
//! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам.
|
||
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); }
|
||
|
||
//! \~english Assigns the contents of another Col to this column.
|
||
//! \~russian Присваивает этому столбцу содержимое другого столбца.
|
||
inline Col & operator=(const Col & other) {
|
||
if (p_ == other.p_ && col_ == other.col_) return *this;
|
||
const size_t sz = piMin<size_t>(sz_, other.sz_);
|
||
for (size_t i = 0; i < sz; ++i)
|
||
(*p_)[i * step_ + col_] = other[i];
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Assigns the contents of a \a PIVector to this column.
|
||
//! \~russian Присваивает этому столбцу содержимое \a PIVector.
|
||
inline Col & operator=(const PIVector<T> & other) {
|
||
const size_t sz = piMin<size_t>(sz_, other.size());
|
||
for (size_t i = 0; i < sz; ++i)
|
||
(*p_)[i * step_ + col_] = other[i];
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Converts the column to a \a PIVector.
|
||
//! \~russian Преобразует столбец в \a PIVector.
|
||
inline PIVector<T> toVector() const {
|
||
PIVector<T> ret;
|
||
ret.reserve(sz_);
|
||
for (size_t i = 0; i < sz_; i++)
|
||
ret << (*p_)[i * step_ + col_];
|
||
return ret;
|
||
}
|
||
|
||
// --- Поиск в столбце ---
|
||
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if ((*p_)[i * step_ + col_] == e) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if ((*p_)[i * step_ + col_] == e) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if (test((*p_)[i * step_ + col_])) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if (test((*p_)[i * step_ + col_])) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
};
|
||
|
||
//! \class RowConst
|
||
//! \brief
|
||
//! \~english Proxy class representing a single read-only row in a \a PIVector2D.
|
||
//! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D только для чтения.
|
||
class RowConst {
|
||
friend class PIVector2D<T>;
|
||
|
||
private:
|
||
inline RowConst(const PIVector2D<T> * p, size_t row): p_(&(p->mat)) {
|
||
st_ = p->cols_ * row;
|
||
sz_ = p->cols_;
|
||
}
|
||
const PIVector<T> * p_;
|
||
size_t st_, sz_;
|
||
|
||
public:
|
||
//! \~english Size of the row (number of columns).
|
||
//! \~russian Размер строки (количество столбцов).
|
||
inline size_t size() const { return sz_; }
|
||
|
||
//! \~english Const access to the element at the given column index within the row.
|
||
//! \~russian Константный доступ к элементу по заданному индексу столбца в строке.
|
||
inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; }
|
||
|
||
//! \~english Returns a const pointer to the row data starting at an optional offset.
|
||
//! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения.
|
||
inline const T * data(size_t index = 0) const { return p_->data(st_ + index); }
|
||
|
||
//! \~english Converts the row to a \a PIVector.
|
||
//! \~russian Преобразует строку в \a PIVector.
|
||
inline PIVector<T> toVector() const { return PIVector<T>(p_->data(st_), sz_); }
|
||
|
||
// --- Поиск в строке (только чтение) ---
|
||
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if ((*p_)[st_ + i] == e) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if ((*p_)[st_ + i] == e) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if (test((*p_)[st_ + i])) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if (test((*p_)[st_ + i])) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
};
|
||
|
||
//! \class ColConst
|
||
//! \brief
|
||
//! \~english Proxy class representing a single read-only column in a \a PIVector2D.
|
||
//! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D только для чтения.
|
||
class ColConst {
|
||
friend class PIVector2D<T>;
|
||
|
||
private:
|
||
inline ColConst(const PIVector2D<T> * p, size_t col): p_(&(p->mat)) {
|
||
step_ = p->cols_;
|
||
col_ = col;
|
||
sz_ = p->rows_;
|
||
}
|
||
const PIVector<T> * p_;
|
||
size_t step_, col_, sz_;
|
||
|
||
public:
|
||
//! \~english Size of the column (number of rows).
|
||
//! \~russian Размер столбца (количество строк).
|
||
inline size_t size() const { return sz_; }
|
||
|
||
//! \~english Const access to the element at the given row index within the column.
|
||
//! \~russian Константный доступ к элементу по заданному индексу строки в столбце.
|
||
inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; }
|
||
|
||
//! \~english Returns a const pointer to the column data starting at an optional row offset.
|
||
//! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам.
|
||
inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); }
|
||
|
||
//! \~english Converts the column to a \a PIVector.
|
||
//! \~russian Преобразует столбец в \a PIVector.
|
||
inline PIVector<T> toVector() const {
|
||
PIVector<T> ret;
|
||
ret.reserve(sz_);
|
||
for (size_t i = 0; i < size(); i++)
|
||
ret << (*p_)[i * step_ + col_];
|
||
return ret;
|
||
}
|
||
|
||
// --- Поиск в столбце (только чтение) ---
|
||
inline ssize_t indexOf(const T & e, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if ((*p_)[i * step_ + col_] == e) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if ((*p_)[i * step_ + col_] == e) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||
if (start < 0) start = 0;
|
||
for (size_t i = (size_t)start; i < sz_; ++i) {
|
||
if (test((*p_)[i * step_ + col_])) return (ssize_t)i;
|
||
}
|
||
return -1;
|
||
}
|
||
inline ssize_t lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||
ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start;
|
||
for (ssize_t i = from; i >= 0; --i) {
|
||
if (test((*p_)[i * step_ + col_])) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
};
|
||
|
||
//! \~english Returns a reference to the element at the given row and column.
|
||
//! \~russian Возвращает ссылку на элемент по заданной строке и столбцу.
|
||
inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; }
|
||
|
||
//! \~english Returns a const reference to the element at the given row and column.
|
||
//! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу.
|
||
inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
||
|
||
//! \~english Returns a const reference to the element at the given row and column (bounds-checked only in debug).
|
||
//! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу (проверка границ только в отладочном режиме).
|
||
inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; }
|
||
|
||
//! \~english Returns a proxy object for the row at the given index for modification.
|
||
//! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации.
|
||
inline Row operator[](size_t index) { return Row(this, index); }
|
||
|
||
//! \~english Returns a proxy object for the row at the given index for read-only access.
|
||
//! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения.
|
||
inline RowConst operator[](size_t index) const { return RowConst(this, index); }
|
||
|
||
//! \~english Returns a pointer to the underlying flat data starting at an optional offset.
|
||
//! \~russian Возвращает указатель на внутренние плоские данные, начиная с опционального смещения.
|
||
inline T * data(size_t index = 0) { return mat.data(index); }
|
||
|
||
//! \~english Returns a const pointer to the underlying flat data starting at an optional offset.
|
||
//! \~russian Возвращает константный указатель на внутренние плоские данные, начиная с опционального смещения.
|
||
inline const T * data(size_t index = 0) const { return mat.data(index); }
|
||
|
||
|
||
//! \~english Returns a proxy object for the row at the given index for modification.
|
||
//! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации.
|
||
inline Row row(size_t index) { return Row(this, index); }
|
||
|
||
//! \~english Returns a proxy object for the row at the given index for read-only access.
|
||
//! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения.
|
||
inline RowConst row(size_t index) const { return RowConst(this, index); }
|
||
|
||
//! \~english Returns a proxy object for the column at the given index for modification.
|
||
//! \~russian Возвращает прокси-объект для столбца по заданному индексу для модификации.
|
||
inline Col col(size_t index) { return Col(this, index); }
|
||
|
||
//! \~english Returns a proxy object for the column at the given index for read-only access.
|
||
//! \~russian Возвращает прокси-объект для столбца по заданному индексу только для чтения.
|
||
inline ColConst col(size_t index) const { return ColConst(this, index); }
|
||
|
||
|
||
//! \~english Replaces a row with the contents of another Row object.
|
||
//! \~russian Заменяет строку содержимым другого объекта Row.
|
||
inline PIVector2D<T> & setRow(size_t row, const Row & other) {
|
||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Replaces a row with the contents of a read-only RowConst object.
|
||
//! \~russian Заменяет строку содержимым объекта RowConst только для чтения.
|
||
inline PIVector2D<T> & setRow(size_t row, const RowConst & other) {
|
||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Replaces a row with the contents of a \a PIVector.
|
||
//! \~russian Заменяет строку содержимым \a PIVector.
|
||
inline PIVector2D<T> & setRow(size_t row, const PIVector<T> & other) {
|
||
const size_t sz = piMin<size_t>(cols_, other.size());
|
||
mat._copyRaw(mat.data(cols_ * row), other.data(), sz);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Appends a new row to the bottom of the array from another Row object.
|
||
//! \~russian Добавляет новую строку в конец массива из другого объекта Row.
|
||
inline PIVector2D<T> & addRow(const Row & other) {
|
||
if (cols_ == 0) cols_ = other.sz_;
|
||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||
const size_t ps = mat.size();
|
||
mat.resize(mat.size() + cols_);
|
||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
||
rows_++;
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Appends a new row to the bottom of the array from a read-only RowConst object.
|
||
//! \~russian Добавляет новую строку в конец массива из объекта RowConst только для чтения.
|
||
inline PIVector2D<T> & addRow(const RowConst & other) {
|
||
if (cols_ == 0) cols_ = other.sz_;
|
||
const size_t sz = piMin<size_t>(cols_, other.sz_);
|
||
const size_t ps = mat.size();
|
||
mat.resize(mat.size() + cols_);
|
||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
||
rows_++;
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Appends a new row to the bottom of the array from a \a PIVector.
|
||
//! \~russian Добавляет новую строку в конец массива из \a PIVector.
|
||
inline PIVector2D<T> & addRow(const PIVector<T> & other) {
|
||
if (cols_ == 0) cols_ = other.size();
|
||
const size_t sz = piMin<size_t>(cols_, other.size());
|
||
const size_t ps = mat.size();
|
||
mat.resize(mat.size() + cols_);
|
||
mat._copyRaw(mat.data(ps), other.data(), sz);
|
||
rows_++;
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Resizes the 2D array to new dimensions.
|
||
//! \~russian Изменяет размер двумерного массива.
|
||
//! \details
|
||
//! \~english If the new dimensions are larger, new elements are filled with `f`.
|
||
//! If they are smaller, the array is truncated.
|
||
//! \~russian Если новые размеры больше, новые элементы заполняются `f`.
|
||
//! Если они меньше, массив обрезается.
|
||
inline PIVector2D<T> & resize(size_t rows, size_t cols, const T & f = T()) {
|
||
if (rows == rows_ && cols == cols_) return *this;
|
||
PIVector2D<T> tmp(rows, cols, f);
|
||
size_t copyRows = piMin(rows_, rows);
|
||
size_t copyCols = piMin(cols_, cols);
|
||
for (size_t r = 0; r < copyRows; ++r) {
|
||
for (size_t c = 0; c < copyCols; ++c) {
|
||
tmp.element(r, c) = element(r, c);
|
||
}
|
||
}
|
||
swap(tmp);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Equality operator.
|
||
//! \~russian Оператор равенства.
|
||
inline bool operator==(const PIVector2D<T> & t) const {
|
||
if (cols_ != t.cols_ || rows_ != t.rows_) return false;
|
||
return mat == t.mat;
|
||
}
|
||
|
||
//! \~english Inequality operator.
|
||
//! \~russian Оператор неравенства.
|
||
inline bool operator!=(const PIVector2D<T> & t) const { return !(*this == t); }
|
||
|
||
//! \~english Converts the 2D array to a vector of vectors (PIVector<PIVector<T>>).
|
||
//! \~russian Преобразует двумерный массив в вектор векторов (PIVector<PIVector<T>>).
|
||
inline PIVector<PIVector<T>> toVectors() const {
|
||
PIVector<PIVector<T>> ret;
|
||
ret.reserve(rows_);
|
||
for (size_t i = 0; i < rows_; ++i)
|
||
ret << PIVector<T>(mat.data(i * cols_), cols_);
|
||
return ret;
|
||
}
|
||
|
||
//! \~english Returns a const reference to the underlying flat \a PIVector.
|
||
//! \~russian Возвращает константную ссылку на внутренний плоский \a PIVector.
|
||
inline const PIVector<T> & asPlainVector() const { return mat; }
|
||
|
||
//! \~english Returns a reference to the underlying flat \a PIVector.
|
||
//! \~russian Возвращает ссылку на внутренний плоский \a PIVector.
|
||
inline PIVector<T> & asPlainVector() { return mat; }
|
||
|
||
//! \~english Returns a copy of the underlying flat \a PIVector.
|
||
//! \~russian Возвращает копию внутреннего плоского \a PIVector.
|
||
inline PIVector<T> toPlainVector() const { return mat; }
|
||
|
||
//! \~english Swaps this 2D array with another.
|
||
//! \~russian Меняет местами этот двумерный массив с другим.
|
||
inline void swap(PIVector2D<T> & other) {
|
||
mat.swap(other.mat);
|
||
piSwap<size_t>(rows_, other.rows_);
|
||
piSwap<size_t>(cols_, other.cols_);
|
||
}
|
||
|
||
//! \internal
|
||
template<typename T1 = T, typename std::enable_if<std::is_trivially_copyable<T1>::value, int>::type = 0>
|
||
inline PIVector2D<T> & _resizeRaw(size_t r, size_t c) {
|
||
rows_ = r;
|
||
cols_ = c;
|
||
mat._resizeRaw(r * c);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Clears the array, removing all elements and setting dimensions to 0.
|
||
//! \~russian Очищает массив, удаляя все элементы и устанавливая размеры в 0.
|
||
inline void clear() {
|
||
rows_ = cols_ = 0;
|
||
mat.clear();
|
||
}
|
||
|
||
|
||
//! \~english Checks if the underlying flat vector contains the element `e`.
|
||
//! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`.
|
||
inline bool contains(const T & e, ssize_t start = 0) const { return mat.contains(e, start); }
|
||
|
||
//! \~english Checks if the underlying flat vector contains all elements of `v`.
|
||
//! \~russian Проверяет, содержит ли внутренний плоский вектор все элементы `v`.
|
||
inline bool contains(const PIVector<T> & v, ssize_t start = 0) const { return mat.contains(v, start); }
|
||
|
||
//! \~english Counts occurrences of `e` in the underlying flat vector.
|
||
//! \~russian Подсчитывает количество вхождений `e` во внутреннем плоском векторе.
|
||
inline int entries(const T & e, ssize_t start = 0) const { return mat.entries(e, start); }
|
||
|
||
//! \~english Counts elements in the flat vector that pass the `test`.
|
||
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
|
||
inline int entries(std::function<bool(const T & e)> test, ssize_t start = 0) const { return mat.entries(test, start); }
|
||
|
||
|
||
//! \~english Returns the first index (row, col) of `e` in the 2D array.
|
||
//! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||
inline PIPair<ssize_t, ssize_t> indexOf(const T & e, ssize_t start = 0) const {
|
||
ssize_t flat = mat.indexOf(e, start);
|
||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||
}
|
||
|
||
//! \~english Returns the first index (row, col) in the 2D array that passes the `test`.
|
||
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||
inline PIPair<ssize_t, ssize_t> indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||
ssize_t flat = mat.indexWhere(test, start);
|
||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||
}
|
||
|
||
//! \~english Returns the last index (row, col) of `e` in the 2D array.
|
||
//! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве.
|
||
inline PIPair<ssize_t, ssize_t> lastIndexOf(const T & e, ssize_t start = -1) const {
|
||
ssize_t flat = mat.lastIndexOf(e, start);
|
||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||
}
|
||
|
||
//! \~english Returns the last index (row, col) in the 2D array that passes the `test`.
|
||
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||
inline PIPair<ssize_t, ssize_t> lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||
ssize_t flat = mat.lastIndexWhere(test, start);
|
||
if (flat < 0 || cols_ == 0) return PIPair<ssize_t, ssize_t>(-1, -1);
|
||
return PIPair<ssize_t, ssize_t>(flat / cols_, flat % cols_);
|
||
}
|
||
|
||
|
||
//! \~english Tests if any element in the flat vector passes the `test`.
|
||
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
|
||
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(test); }
|
||
|
||
//! \~english Tests if all elements in the flat vector pass the `test`.
|
||
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
|
||
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(test); }
|
||
|
||
//! \~english Fills the entire 2D array with copies of `e`.
|
||
//! \~russian Заполняет весь двумерный массив копиями `e`.
|
||
inline PIVector2D<T> & fill(const T & e = T()) {
|
||
mat.fill(e);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Fills the entire 2D array using a generator function `f` based on flat index.
|
||
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
|
||
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
|
||
mat.fill(f);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Same as \a fill().
|
||
//! \~russian То же, что и \a fill().
|
||
inline PIVector2D<T> & assign(const T & e = T()) { return fill(e); }
|
||
|
||
//! \~english Assigns new size and fills with value.
|
||
//! \~russian Задаёт новый размер и заполняет значением.
|
||
inline PIVector2D<T> & assign(size_t rows, size_t cols, const T & f = T()) {
|
||
mat.assign(rows * cols, f);
|
||
rows_ = rows;
|
||
cols_ = cols;
|
||
return *this;
|
||
}
|
||
|
||
|
||
// TODO: исправить - при транспонировании количество строк становится количеством столбцов и наоборот
|
||
//! \~english Returns a transposed 2D array (rows become columns and vice versa).
|
||
//! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот).
|
||
inline PIVector2D<T> transposed() const {
|
||
if (isEmpty()) return PIVector2D<T>();
|
||
PIVector2D<T> result(cols_, rows_);
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
for (size_t c = 0; c < cols_; ++c) {
|
||
result.element(c, r) = element(r, c);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//! \~english Reverses the order of rows in place.
|
||
//! \~russian Изменяет порядок строк на обратный на месте.
|
||
inline PIVector2D<T> & reverseRows() {
|
||
const size_t half = rows_ / 2;
|
||
for (size_t i = 0; i < half; ++i) {
|
||
T * row1 = data(i * cols_);
|
||
T * row2 = data((rows_ - 1 - i) * cols_);
|
||
for (size_t j = 0; j < cols_; ++j) {
|
||
piSwap(row1[j], row2[j]);
|
||
}
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Reverses the order of columns in each row in place.
|
||
//! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте.
|
||
inline PIVector2D<T> & reverseColumns() {
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
Row currentRow = row(r);
|
||
const size_t half = cols_ / 2;
|
||
for (size_t c = 0; c < half; ++c) {
|
||
piSwap<T>(currentRow[c], currentRow[cols_ - 1 - c]);
|
||
}
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Returns a sub-2D array (a range of rows and columns).
|
||
//! \~russian Возвращает подмассив (диапазон строк и столбцов).
|
||
inline PIVector2D<T> getRange(size_t rowStart, size_t rowCount, size_t colStart, size_t colCount) const {
|
||
if (rowStart >= rows_ || colStart >= cols_ || rowCount == 0 || colCount == 0) return PIVector2D<T>();
|
||
size_t actualRowCount = piMin(rowCount, rows_ - rowStart);
|
||
size_t actualColCount = piMin(colCount, cols_ - colStart);
|
||
|
||
PIVector2D<T> result(actualRowCount, actualColCount);
|
||
for (size_t r = 0; r < actualRowCount; ++r) {
|
||
for (size_t c = 0; c < actualColCount; ++c) {
|
||
result.element(r, c) = element(rowStart + r, colStart + c);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//! \~english Applies a function to each element and returns a new 2D array of a different type.
|
||
//! \~russian Применяет функцию к каждому элементу и возвращает новый двумерный массив другого типа.
|
||
template<typename ST>
|
||
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
||
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(f));
|
||
}
|
||
|
||
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
|
||
//! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив.
|
||
template<typename ST>
|
||
inline PIVector2D<ST> mapIndexed(std::function<ST(size_t row, size_t col, const T & e)> f) const {
|
||
PIVector<ST> mappedMat;
|
||
mappedMat.reserve(size());
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
for (size_t c = 0; c < cols_; ++c) {
|
||
mappedMat << f(r, c, element(r, c));
|
||
}
|
||
}
|
||
return PIVector2D<ST>(rows_, cols_, std::move(mappedMat));
|
||
}
|
||
|
||
// --- Итерация по строкам и столбцам ---
|
||
//! \~english Applies a function to each row (modifiable).
|
||
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
|
||
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) {
|
||
for (size_t r = 0; r < rows_; ++r)
|
||
f(row(r));
|
||
return *this;
|
||
}
|
||
//! \~english Applies a function to each row (read-only).
|
||
//! \~russian Применяет функцию к каждой строке (только чтение).
|
||
inline void forEachRow(std::function<void(RowConst)> f) const {
|
||
for (size_t r = 0; r < rows_; ++r)
|
||
f(row(r));
|
||
}
|
||
//! \~english Applies a function to each column (modifiable).
|
||
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
|
||
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
|
||
for (size_t c = 0; c < cols_; ++c)
|
||
f(col(c));
|
||
return *this;
|
||
}
|
||
//! \~english Applies a function to each column (read-only).
|
||
//! \~russian Применяет функцию к каждому столбцу (только чтение).
|
||
inline void forEachColumn(std::function<void(ColConst)> f) const {
|
||
for (size_t c = 0; c < cols_; ++c)
|
||
f(col(c));
|
||
}
|
||
|
||
//! \~english Accumulates a value across all elements.
|
||
//! \~russian Аккумулирует значение по всем элементам.
|
||
template<typename ST>
|
||
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||
return mat.template reduce<ST>(f, initial);
|
||
}
|
||
|
||
//! \~english Accumulates a value across all elements with indices.
|
||
//! \~russian Аккумулирует значение по всем элементам с индексами.
|
||
template<typename ST>
|
||
inline ST reduceIndexed(std::function<ST(size_t row, size_t col, const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||
ST ret(initial);
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
for (size_t c = 0; c < cols_; ++c) {
|
||
ret = f(r, c, element(r, c), ret);
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
//! \~english Removes a row from the 2D array.
|
||
//! \~russian Удаляет строку из двумерного массива.
|
||
inline PIVector2D<T> & removeRow(size_t row) {
|
||
if (row >= rows_) return *this;
|
||
size_t startIdx = row * cols_;
|
||
mat.remove(startIdx, cols_);
|
||
rows_--;
|
||
if (rows_ == 0) cols_ = 0;
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Removes a column from the 2D array.
|
||
//! \~russian Удаляет столбец из двумерного массива.
|
||
inline PIVector2D<T> & removeColumn(size_t col) {
|
||
if (col >= cols_ || rows_ == 0) return *this;
|
||
PIVector2D<T> result(rows_, cols_ - 1);
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
for (size_t c = 0, nc = 0; c < cols_; ++c) {
|
||
if (c == col) continue;
|
||
result.element(r, nc++) = element(r, c);
|
||
}
|
||
}
|
||
swap(result);
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Removes all rows that satisfy a condition.
|
||
//! \~russian Удаляет все строки, удовлетворяющие условию.
|
||
inline PIVector2D<T> & removeRowsWhere(std::function<bool(const RowConst &)> test) {
|
||
ssize_t r = rows_ - 1;
|
||
while (r >= 0) {
|
||
if (test(RowConst(this, r))) {
|
||
removeRow(r);
|
||
}
|
||
--r;
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
//! \~english Removes all columns that satisfy a condition.
|
||
//! \~russian Удаляет все столбцы, удовлетворяющие условию.
|
||
inline PIVector2D<T> & removeColumnsWhere(std::function<bool(const ColConst &)> test) {
|
||
ssize_t c = cols_ - 1;
|
||
while (c >= 0) {
|
||
if (test(ColConst(this, c))) {
|
||
removeColumn(c);
|
||
}
|
||
--c;
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
|
||
//! \~english Returns a new 2D array containing only the rows that pass the test.
|
||
//! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку.
|
||
inline PIVector2D<T> filterRows(std::function<bool(const RowConst &)> test) const {
|
||
PIVector2D<T> result;
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
RowConst currentRow = row(r);
|
||
if (test(currentRow)) {
|
||
result.addRow(currentRow);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//! \~english Returns a new 2D array containing only the columns that pass the test.
|
||
//! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку.
|
||
inline PIVector2D<T> filterColumns(std::function<bool(const ColConst &)> test) const {
|
||
if (isEmpty()) return PIVector2D<T>();
|
||
PIVector<size_t> goodCols;
|
||
for (size_t c = 0; c < cols_; ++c) {
|
||
if (test(col(c))) {
|
||
goodCols << c;
|
||
}
|
||
}
|
||
PIVector2D<T> result(rows_, goodCols.size());
|
||
for (size_t r = 0; r < rows_; ++r) {
|
||
for (size_t gc = 0; gc < goodCols.size(); ++gc) {
|
||
result.element(r, gc) = element(r, goodCols[gc]);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//! \~english Returns a new 2D array (as a single row) containing only the elements that pass the test.
|
||
//! \~russian Возвращает новый двумерный массив (в виде одной строки), содержащий только элементы, прошедшие проверку.
|
||
inline PIVector2D<T> filterElements(std::function<bool(const T &)> test) const {
|
||
PIVector<T> filtered = mat.filter(test);
|
||
if (filtered.isEmpty()) return PIVector2D<T>();
|
||
return PIVector2D<T>(1, filtered.size(), filtered);
|
||
}
|
||
|
||
protected:
|
||
size_t rows_, cols_;
|
||
PIVector<T> mat;
|
||
};
|
||
|
||
|
||
//! \relatesalso PICout
|
||
//! \~english Output operator for \a PIVector2D to \a PICout.
|
||
//! \~russian Оператор вывода \a PIVector2D в \a PICout.
|
||
template<typename T>
|
||
inline PICout operator<<(PICout s, const PIVector2D<T> & v) {
|
||
s.saveAndSetControls(0);
|
||
s << "{";
|
||
for (size_t i = 0; i < v.rows(); ++i) {
|
||
s << "{ ";
|
||
for (size_t j = 0; j < v.cols(); ++j) {
|
||
s << v[i][j];
|
||
if (j < v.cols() - 1) s << ", ";
|
||
}
|
||
s << " }";
|
||
if (i < v.rows() - 1) s << PICoutManipulators::NewLine;
|
||
}
|
||
if (v.isEmpty()) s << "{ }";
|
||
s << "}";
|
||
s.restoreControls();
|
||
return s;
|
||
}
|
||
|
||
//! \}
|
||
|
||
#endif // PIVECTOR2D_H
|