File size: 11,373 Bytes
a5ffdcd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/*******************************************************************************
 *
 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());
}