LibreCAD / librecad /src /lib /gui /lc_coordinates_mapper.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
a5ffdcd verified
/*******************************************************************************
*
This file is part of the LibreCAD project, a 2D CAD program
Copyright (C) 2025 LibreCAD.org
Copyright (C) 2025 sand1024
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
******************************************************************************/
#include "lc_coordinates_mapper.h"
#include "lc_rect.h"
#include "rs_math.h"
#include "rs_vector.h"
LC_CoordinatesMapper::LC_CoordinatesMapper(){
setXAxisAngle(0.0);
}
RS_Vector LC_CoordinatesMapper::doWCS2UCS(const RS_Vector &worldCoordinate) const {
// the code below is unwrapped equivalent to this
/*
RS_Vector wcs = RS_Vector(worldX, worldY);
RS_Vector newPos = wcs-ucsOrigin;
newPos.rotate(xAxisAngle);
uiY = newPos.x;
uiX = newPos.y;
*/
return RS_Vector{worldCoordinate}.move(-m_ucsOrigin).rotate(m_ucsRotation);
}
void LC_CoordinatesMapper::doWCS2UCS(double worldX, double worldY, double &ucsX, double &ucsY) const {
// the code below is unwrapped equivalent to this
/*
RS_Vector wcs = RS_Vector(worldX, worldY);
RS_Vector newPos = wcs-m_ucsOrigin;
newPos.rotate(xAxisAngle);
uiY = newPos.x;
uiX = newPos.y;
*/
double ucsPositionX = worldX - m_ucsOrigin.x;
double ucsPositionY = worldY - m_ucsOrigin.y;
ucsX = ucsPositionX * cosXAngle - ucsPositionY * sinXAngle;
ucsY = ucsPositionX * sinXAngle + ucsPositionY * cosXAngle;
}
// todo - sand - ucs - inline calculations
RS_Vector LC_CoordinatesMapper::doWCSDelta2UCSDelta(const RS_Vector &worldDelta) const {
return worldDelta.rotated(xAxisAngle);
}
void LC_CoordinatesMapper::doWCSDelta2UCSDelta(const RS_Vector &worldDelta, double &ucsDX, double &ucsDY) const {
double magnitude = worldDelta.magnitude();
double angle = worldDelta.angle();
double ucsAngle = angle + xAxisAngle;
ucsDX = magnitude*cos(ucsAngle);
ucsDY = magnitude*sin(ucsAngle);
}
RS_Vector LC_CoordinatesMapper::doUCSDelta2WCSDelta(const RS_Vector &ucsDelta) const {
return ucsDelta.rotated(-xAxisAngle);
}
void LC_CoordinatesMapper::doUCSDelta2WCSDelta(const RS_Vector &ucsDelta, double &wcsDX, double &wcsDY) const {
double magnitude = ucsDelta.magnitude();
double angle = ucsDelta.angle();
double ucsAngle = angle - xAxisAngle;
wcsDX = magnitude*cos(ucsAngle);
wcsDY = magnitude*sin(ucsAngle);
}
RS_Vector LC_CoordinatesMapper::doUCS2WCS(const RS_Vector &ucsCoordinate) const {
// code is equivalent to
/*
RS_Vector newPos = ucsCoordinate;
newPos.rotate(-xAxisAngle);
worldCoordinate = newPos + ucsOrigin;
*/
return ucsCoordinate.rotated(m_AxisNegRotation) + m_ucsOrigin;
}
void LC_CoordinatesMapper::doUCS2WCS(double ucsX, double ucsY, double &worldX, double &worldY) const{
// code is equivalent to
/*
RS_Vector ucsCoordinate = RS_Vector(ucsX, ucsY);
ucsCoordinate.rotate(-xAxisAngle);
RS_Vector world = ucsCoordinate + ucsOrigin;
worldX = world.x;
worldY = world.y;
*/
double wcsX = ucsX * cosNegativeXAngle - ucsY * sinNegativeXAngle;
double wcsY = ucsX * sinNegativeXAngle + ucsY * cosNegativeXAngle;
worldX = wcsX + m_ucsOrigin.x;
worldY = wcsY + m_ucsOrigin.y;
}
void LC_CoordinatesMapper::setXAxisAngle(double angle){
xAxisAngle = angle;
xAxisAngleDegrees = RS_Math::rad2deg(angle);
m_ucsRotation = RS_Vector{angle};
m_AxisNegRotation = m_ucsRotation;
m_AxisNegRotation.y = - m_ucsRotation.y;
}
void LC_CoordinatesMapper::update(const RS_Vector &origin, double angle) {
m_ucsOrigin = origin;
setXAxisAngle(angle);
}
const RS_Vector &LC_CoordinatesMapper::getUcsOrigin() const {
return m_ucsOrigin;
}
void LC_CoordinatesMapper::setUcsOrigin(const RS_Vector& origin)
{
m_ucsOrigin = origin;
}
double LC_CoordinatesMapper::toWorldAngle(double ucsAngle) const{
return m_hasUcs ? ucsAngle - xAxisAngle : ucsAngle;
}
double LC_CoordinatesMapper::toWorldAngleDegrees(double ucsAngle) const{
return m_hasUcs ? ucsAngle - xAxisAngleDegrees : ucsAngle;
}
RS_Vector LC_CoordinatesMapper::restrictHorizontal(const RS_Vector &baseWCSPoint, const RS_Vector &wcsCoord) const {
if (m_hasUcs) {
RS_Vector ucsBase = toUCS(baseWCSPoint);
RS_Vector ucsCoord = toUCS(wcsCoord);
return doUCS2WCS({ucsCoord.x, ucsBase.y});
}
else{
return RS_Vector(wcsCoord.x, baseWCSPoint.y);
}
}
RS_Vector LC_CoordinatesMapper::restrictVertical(const RS_Vector &baseWCSPoint, const RS_Vector &wcsCoord) const {
if (m_hasUcs) {
RS_Vector ucsBase = toUCS(baseWCSPoint);
RS_Vector ucsCoord = toUCS(wcsCoord);
return doUCS2WCS({ucsBase.x, ucsCoord.y});
}
else{
return RS_Vector(baseWCSPoint.x, wcsCoord.y);
}
}
void LC_CoordinatesMapper::ucsBoundingBox(const RS_Vector& wcsMin, const RS_Vector&wcsMax, RS_Vector& ucsMin, RS_Vector& ucsMax) const{
if (m_hasUcs) {
/* This implementation does not work, too aggressive clipping of entities
LC_Rect ucsRect{toUCS(wcsMin), toUCS(wcsMax)};
ucsRect.merge(toUCS({wcsMin.x, wcsMax.y}));
ucsRect.merge(toUCS({wcsMax.x, wcsMin.y}));
ucsMin = ucsRect.minP();
ucsMax = ucsRect.maxP();
*/
// this implementation works properly and
RS_Vector ucsCorner1 = toUCS(wcsMin);
RS_Vector ucsCorner3 = toUCS(wcsMax);
RS_Vector ucsCorner2 = RS_Vector(ucsCorner1.x, ucsCorner3.y);
RS_Vector ucsCorner4 = RS_Vector(ucsCorner3.x, ucsCorner1.y);
double minX, maxX;
double minY, maxY;
maxX = std::max(ucsCorner1.x, ucsCorner3.x);
maxX = std::max(ucsCorner2.x, maxX);
maxX = std::max(ucsCorner4.x, maxX);
minX = std::min(ucsCorner1.x, ucsCorner3.x);
minX = std::min(ucsCorner2.x, minX);
minX = std::min(ucsCorner4.x, minX);
maxY = std::max(ucsCorner1.y, ucsCorner3.y);
maxY = std::max(ucsCorner2.y, maxY);
maxY = std::max(ucsCorner4.y, maxY);
minY = std::min(ucsCorner1.y, ucsCorner3.y);
minY = std::min(ucsCorner2.y, minY);
minY = std::min(ucsCorner4.y, minY);
ucsMin = RS_Vector(minX, minY);
ucsMax = RS_Vector(maxX, maxY);
}
else{
ucsMin = wcsMin;
ucsMax = wcsMax;
}
}
void LC_CoordinatesMapper::worldBoundingBox(const RS_Vector& ucsMin, const RS_Vector&ucsMax, RS_Vector& worldMin, RS_Vector& worldMax) const{
if (m_hasUcs) {
/* This does not work right, too aggressive clipping of entities
LC_Rect worldRect{toWorld(ucsMin), toWorld(ucsMax)};
worldRect.merge(toWorld({ucsMin.x, ucsMax.y}));
worldRect.merge(toWorld({ucsMax.x, ucsMin.y}));
worldMin = worldRect.minP();
worldMax = worldRect.maxP();
*/
// This implementation is correct
RS_Vector ucsCorner1 = ucsMin;
RS_Vector ucsCorner3 = ucsMax;
RS_Vector ucsCorner2 = RS_Vector(ucsCorner1.x, ucsCorner3.y);
RS_Vector ucsCorner4 = RS_Vector(ucsCorner3.x, ucsCorner1.y);
RS_Vector worldCorner1 = toWorld(ucsCorner1);
RS_Vector worldCorner2 = toWorld(ucsCorner2);
RS_Vector worldCorner3 = toWorld(ucsCorner3);
RS_Vector worldCorner4 = toWorld(ucsCorner4);
double minX, maxX;
double minY, maxY;
maxX = std::max(worldCorner1.x, worldCorner3.x);
maxX = std::max(worldCorner2.x, maxX);
maxX = std::max(worldCorner4.x, maxX);
minX = std::min(worldCorner1.x, worldCorner3.x);
minX = std::min(worldCorner2.x, minX);
minX = std::min(worldCorner4.x, minX);
maxY = std::max(worldCorner1.y, worldCorner3.y);
maxY = std::max(worldCorner2.y, maxY);
maxY = std::max(worldCorner4.y, maxY);
minY = std::min(worldCorner1.y, worldCorner3.y);
minY = std::min(worldCorner2.y, minY);
minY = std::min(worldCorner4.y, minY);
worldMin = RS_Vector(minX, minY);
worldMax = RS_Vector(maxX, maxY);
}
else{
worldMin = ucsMin;
worldMax = ucsMax;
}
}
double LC_CoordinatesMapper::toUCSAngle(double wcsAngle) const{
return m_hasUcs ? wcsAngle + xAxisAngle : wcsAngle;
}
double LC_CoordinatesMapper::toUCSAngleDegrees(double wcsAngle) const{
return m_hasUcs ? wcsAngle + xAxisAngleDegrees : wcsAngle;
}
void LC_CoordinatesMapper::toUCSDelta(const RS_Vector &worldDelta, double &ucsDX, double &ucsDY) const {
if (m_hasUcs){
doWCSDelta2UCSDelta(worldDelta, ucsDX, ucsDY);
}
else{
ucsDX = worldDelta.x;
ucsDY = worldDelta.y;
}
}
RS_Vector LC_CoordinatesMapper::toUCSDelta(const RS_Vector& worldDelta) const {
return m_hasUcs ? doWCSDelta2UCSDelta(worldDelta) : worldDelta;
}
RS_Vector LC_CoordinatesMapper::toWorldDelta(const RS_Vector& ucsDelta) const {
return m_hasUcs ? doUCSDelta2WCSDelta(ucsDelta) : ucsDelta;
}
RS_Vector LC_CoordinatesMapper::toUCS(const RS_Vector& wcsPos) const{
return m_hasUcs ? doWCS2UCS(wcsPos) : wcsPos;
}
void LC_CoordinatesMapper::toUCS(const RS_Vector& wcsPos, double& ucsX, double &ucsY) const{
if (m_hasUcs){
doWCS2UCS(wcsPos.x, wcsPos.y, ucsX, ucsY);
}
else{
ucsX = wcsPos.x;
ucsY = wcsPos.y;
}
}
RS_Vector LC_CoordinatesMapper::toWorld(double ucsX, double ucsY) const{
const RS_Vector ucsPosition{ucsX, ucsY};
return m_hasUcs ? doUCS2WCS(ucsPosition) : ucsPosition;
}
RS_Vector LC_CoordinatesMapper::toWorld(const RS_Vector& ucsPos) const{
return m_hasUcs ? doUCS2WCS(ucsPos) : ucsPos;
}
/**
* Transforms absolute angle in UCS (with zero at 3.pm) to angle in UCS basis (with user defined base zero angle)
* @param ucsAbsAngle
* @param baseAngle
* @param counterclockwise
* @return
*/
double LC_CoordinatesMapper::toUCSBasisAngle(double ucsAbsAngle, double baseAngle, bool counterclockwise) {
const double ucsBasisAngle = ucsAbsAngle - baseAngle;
return counterclockwise ? ucsBasisAngle : M_PI * 2 - ucsBasisAngle;
}
/**
* transforms angle in ucs basis (with user defined base zero angle) to absolute LC-standard angle value (with zero of angles at at 3.pm)
* @param ucsBasisAngle
* @param baseAngle
* @param conterclockwise
* @return
*/
double LC_CoordinatesMapper::toUCSAbsAngle(double ucsBasisAngle, double baseAngle, bool counterclockwise) {
const double ucsAbsAngle = ucsBasisAngle + baseAngle;
return counterclockwise ? ucsAbsAngle : M_PI * 2 - ucsAbsAngle;
}
void LC_CoordinatesMapper::apply(LC_CoordinatesMapper *other) {
m_hasUcs = other->hasUCS();
update(other->getUcsOrigin(), other->getXAxisAngle());
}