Loading files
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBREPCB_CORE_TRANSFORM_H
#define LIBREPCB_CORE_TRANSFORM_H
/*******************************************************************************
* Includes
******************************************************************************/
#include "../geometry/path.h"
#include "../types/angle.h"
#include "../types/point.h"
#include <QtCore>
/*******************************************************************************
* Namespace / Forward Declarations
******************************************************************************/
namespace librepcb {
class Layer;
/*******************************************************************************
* Class Transform
******************************************************************************/
/**
* @brief Helper class to perform coordinate transformation with various types
*
* The class is similar to `QTransform`, but with the following differences:
*
* - It transforms mainly LibrePCB types instead of Qt types (e.g.
* ::librepcb::Point instead of `QPointF`).
* - It does not allow arbitrary transformations, but only mirror, rotate
* and translate. The order of the transformation is not configurable, it
* is hardcoded to the order of transformations applied to symbols within
* a schematic, and to footprints within a board. This order is:
* mirror horizontally (negating X-coordinate) -> rotate CCW -> translate.
*
* Long story short, this class converts symbol- or footprint coordinates
* into schematic- resp. board coordinates.
*/
class Transform {
public:
// Constructors / Destructor
/**
* @brief (Default) construdtor
*
* @param position Transformation position.
* @param rotation Transformation rotation.
* @param mirrored Transformation mirror state.
*/
Transform(const Point& position = Point(0, 0),
const Angle& rotation = Angle(0), bool mirrored = false) noexcept
: mPosition(position), mRotation(rotation), mMirrored(mirrored) {}
/**
* @brief Constructor to copy the transformation of an object
*
* @tparam T Any type which provides the methods `Point get Position()`,
* `Angle getRotation()` and `bool getMirrored()`.
* @param obj Any object of the given type. The transformation is copied
* from the provided object.
*/
template <typename T>
explicit Transform(const T& obj)
: mPosition(obj.getPosition()),
mRotation(obj.getRotation()),
mMirrored(obj.getMirrored()) {}
/**
* @brief Copy constructor
*
* @param other Object to copy.
*/
Transform(const Transform& other) noexcept
: mPosition(other.mPosition),
mRotation(other.mRotation),
mMirrored(other.mMirrored) {}
/**
* @brief Destructor
*/
~Transform() noexcept {}
// Getters
const Point& getPosition() const noexcept { return mPosition; }
const Angle& getRotation() const noexcept { return mRotation; }
bool getMirrored() const noexcept { return mMirrored; }
// Setters
void setPosition(const Point& position) noexcept { mPosition = position; }
void setRotation(const Angle& rotation) noexcept { mRotation = rotation; }
void setMirrored(bool mirrored) noexcept { mMirrored = mirrored; }
// General Methods
/**
* @brief Map a given mirror state to the transformed coordinate system
*
* @param mirror The mirror state to map.
* @return The passed mirror state inverted if the transformation is
* mirroring, otherwise not inverted.
*/
bool map(bool mirror) const noexcept;
/**
* @brief Map a given angle to the transformed coordinate system
*
* @note Intended for mirrorable objects (e.g. footprint pads), which expects
* the mapped object to mirror its geometry.
*
* @param angle The angle to map.
* @return The passed angle, mirrored horizontally if the transformation is
* mirroring and rotated by the transformations rotation.
*/
Angle mapMirrorable(const Angle& angle) const noexcept;
/**
* @brief Map a given angle to the transformed coordinate system
*
* @note Intended for non-mirrorable objects (e.g. symbol pins), which will
* cause the mirroring to be "emulated" by a 180° rotation.
*
* @param angle The angle to map.
* @return The passed angle, mirrored horizontally if the transformation is
* mirroring and rotated by the transformations rotation.
*/
Angle mapNonMirrorable(const Angle& angle) const noexcept;
/**
* @brief Map a given point to the transformed coordinate system
*
* @param point The point to map.
* @return The passed point, mirrored horizontally if the transformation is
* mirroring, rotated by the transformations rotation, and translated
* by the transformation offset.
*/
Point map(const Point& point) const noexcept;
/**
* @brief Map a given path to the transformed coordinate system
*
* @param path The path to map.
* @return The passed path, mirrored horizontally if the transformation is
* mirroring, rotated by the transformations rotation, and translated
* by the transformation offset.
*/
Path map(const Path& path) const noexcept;
/**
* @brief Map a given path to the transformed coordinate system
*
* @param path The path to map.
* @return The passed path, mirrored horizontally if the transformation is
* mirroring, rotated by the transformations rotation, and translated
* by the transformation offset.
*/
NonEmptyPath map(const NonEmptyPath& path) const noexcept;
/**
* @brief Map a given layer to the transformed coordinate system
*
* @param layer The layer to map.
* @return The mirrored layer if it's a symetric layer and the
* transformation is mirroring, otherwise the layer is returned as-is.
*/
const Layer& map(const Layer& layer) const noexcept;
/**
* @brief Map all items of a container to the transformed coordinate system
*
* @tparam Container type.
* @param container The items to map.
* @return The passed items, mirrored horizontally if the transformation is
* mirroring, rotated by the transformations rotation, and translated
* by the transformation offset.
*/
template <typename T>
T map(const T& container) const noexcept {
T copy = container;
for (auto& item : copy) {
item = map(item);
}
return copy;
}
/**
* @brief Map a given Qt object in pixels to the transformed coordinate system
*
* @param obj The Qt object (in pixel coordinates) to map, e.g. QPoint,
* QPainterPath, ...).
* @return The passed object, mirrored horizontally if the transformation is
* mirroring, rotated by the transformations rotation, and translated
* by the transformation offset.
*/
template <typename T>
T mapPx(const T& obj) const noexcept {
QTransform t;
t.translate(mPosition.toPxQPointF().x(), mPosition.toPxQPointF().y());
t.rotate(-mRotation.toDeg());
if (mMirrored) {
t.scale(-1, 1);
}
return t.map(obj);
}
// Operator Overloadings
bool operator==(const Transform& rhs) const noexcept {
return (mPosition == rhs.mPosition) && (mRotation == rhs.mRotation) &&
(mMirrored == rhs.mMirrored);
}
bool operator!=(const Transform& rhs) const noexcept {
return !(*this == rhs);
}
Transform& operator=(const Transform& rhs) noexcept {
mPosition = rhs.mPosition;
mRotation = rhs.mRotation;
mMirrored = rhs.mMirrored;
return *this;
}
private: // Data
Point mPosition;
Angle mRotation;
bool mMirrored;
};
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb
#endif
2955876e1This file is pinned to the selected release. Download the original to open it in your design tool.