FreeCAD

Форк
0
/
PathBuilder.cpp 
370 строк · 16.4 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2023 WandererFan <wandererfan@gmail.com>                *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
#include <qmath.h>
26
#endif
27

28
#include <Base/Console.h>
29

30
#include "PathBuilder.h"
31
#include "Rez.h"
32

33
using namespace TechDraw;
34
using namespace TechDrawGui;
35

36

37
QPainterPath PathBuilder::geomToPainterPath(BaseGeomPtr baseGeom, double rot) const
38
{
39
    Q_UNUSED(rot);
40
    QPainterPath path;
41

42
    if (!baseGeom)
43
        return path;
44

45
    switch (baseGeom->getGeomType()) {
46
        case CIRCLE: {
47
            TechDraw::CirclePtr geom = std::static_pointer_cast<TechDraw::Circle>(baseGeom);
48

49
            double x = geom->center.x - geom->radius;
50
            double y = geom->center.y - geom->radius;
51

52
            path.addEllipse(Rez::guiX(x), Rez::guiX(y), Rez::guiX(geom->radius * 2),
53
                            Rez::guiX(geom->radius * 2));//topleft@(x, y) radx, rady
54
        } break;
55
        case ARCOFCIRCLE: {
56
            TechDraw::AOCPtr geom = std::static_pointer_cast<TechDraw::AOC>(baseGeom);
57
            if (baseGeom->getReversed()) {
58
                path.moveTo(Rez::guiX(geom->endPnt.x), Rez::guiX(geom->endPnt.y));
59
                pathArc(path, Rez::guiX(geom->radius), Rez::guiX(geom->radius), 0., geom->largeArc,
60
                        !geom->cw, Rez::guiX(geom->startPnt.x), Rez::guiX(geom->startPnt.y),
61
                        Rez::guiX(geom->endPnt.x), Rez::guiX(geom->endPnt.y));
62
            }
63
            else {
64
                path.moveTo(Rez::guiX(geom->startPnt.x), Rez::guiX(geom->startPnt.y));
65
                pathArc(path, Rez::guiX(geom->radius), Rez::guiX(geom->radius), 0., geom->largeArc,
66
                        geom->cw, Rez::guiX(geom->endPnt.x), Rez::guiX(geom->endPnt.y),
67
                        Rez::guiX(geom->startPnt.x), Rez::guiX(geom->startPnt.y));
68
            }
69
        } break;
70
        case TechDraw::ELLIPSE: {
71
            TechDraw::AOEPtr geom = std::static_pointer_cast<TechDraw::AOE>(baseGeom);
72

73
            // Calculate start and end points as ellipse with theta = 0 and pi
74
            double startX = geom->center.x + geom->major * cos(geom->angle),
75
                   startY = geom->center.y + geom->major * sin(geom->angle),
76
                   endX = geom->center.x - geom->major * cos(geom->angle),
77
                   endY = geom->center.y - geom->major * sin(geom->angle);
78

79
            pathArc(path, Rez::guiX(geom->major), Rez::guiX(geom->minor), geom->angle, false, false,
80
                    Rez::guiX(endX), Rez::guiX(endY), Rez::guiX(startX), Rez::guiX(startY));
81

82
            pathArc(path, Rez::guiX(geom->major), Rez::guiX(geom->minor), geom->angle, false, false,
83
                    Rez::guiX(startX), Rez::guiX(startY), Rez::guiX(endX), Rez::guiX(endY));
84
        } break;
85
        case TechDraw::ARCOFELLIPSE: {
86
            TechDraw::AOEPtr geom = std::static_pointer_cast<TechDraw::AOE>(baseGeom);
87
            if (baseGeom->getReversed()) {
88
                path.moveTo(Rez::guiX(geom->endPnt.x), Rez::guiX(geom->endPnt.y));
89
                pathArc(path, Rez::guiX(geom->major), Rez::guiX(geom->minor), geom->angle,
90
                        geom->largeArc, !geom->cw, Rez::guiX(geom->startPnt.x),
91
                        Rez::guiX(geom->startPnt.y), Rez::guiX(geom->endPnt.x),
92
                        Rez::guiX(geom->endPnt.y));
93
            }
94
            else {
95
                path.moveTo(Rez::guiX(geom->startPnt.x), Rez::guiX(geom->startPnt.y));
96
                pathArc(path, Rez::guiX(geom->major), Rez::guiX(geom->minor), geom->angle,
97
                        geom->largeArc, geom->cw, Rez::guiX(geom->endPnt.x),
98
                        Rez::guiX(geom->endPnt.y), Rez::guiX(geom->startPnt.x),
99
                        Rez::guiX(geom->startPnt.y));
100
            }
101
        } break;
102
        case TechDraw::BEZIER: {
103
            TechDraw::BezierSegmentPtr geom =
104
                std::static_pointer_cast<TechDraw::BezierSegment>(baseGeom);
105
            if (baseGeom->getReversed()) {
106
                if (!geom->pnts.empty()) {
107
                    Base::Vector3d rStart = geom->pnts.back();
108
                    path.moveTo(Rez::guiX(rStart.x), Rez::guiX(rStart.y));
109
                }
110
                if (geom->poles == 2) {
111
                    // Degree 1 bezier = straight line...
112
                    path.lineTo(Rez::guiX(geom->pnts[0].x), Rez::guiX(geom->pnts[0].y));
113
                }
114
                else if (geom->poles == 3) {
115
                    path.quadTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
116
                                Rez::guiX(geom->pnts[0].x), Rez::guiX(geom->pnts[0].y));
117
                }
118
                else if (geom->poles == 4) {
119
                    path.cubicTo(Rez::guiX(geom->pnts[2].x), Rez::guiX(geom->pnts[2].y),
120
                                 Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
121
                                 Rez::guiX(geom->pnts[0].x), Rez::guiX(geom->pnts[0].y));
122
                }
123
                else {//can only handle lines, quads, cubes
124
                    Base::Console().Error("Bad pole count (%d) for BezierSegment\n", geom->poles);
125
                    auto itBez = geom->pnts.begin() + 1;
126
                    for (; itBez != geom->pnts.end(); itBez++) {
127
                        path.lineTo(Rez::guiX((*itBez).x),
128
                                    Rez::guiX((*itBez).y));//show something for debugging
129
                    }
130
                }
131
            }
132
            else {
133
                // Move painter to the beginning
134
                path.moveTo(Rez::guiX(geom->pnts[0].x), Rez::guiX(geom->pnts[0].y));
135

136
                if (geom->poles == 2) {
137
                    // Degree 1 bezier = straight line...
138
                    path.lineTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y));
139
                }
140
                else if (geom->poles == 3) {
141
                    path.quadTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
142
                                Rez::guiX(geom->pnts[2].x), Rez::guiX(geom->pnts[2].y));
143
                }
144
                else if (geom->poles == 4) {
145
                    path.cubicTo(Rez::guiX(geom->pnts[1].x), Rez::guiX(geom->pnts[1].y),
146
                                 Rez::guiX(geom->pnts[2].x), Rez::guiX(geom->pnts[2].y),
147
                                 Rez::guiX(geom->pnts[3].x), Rez::guiX(geom->pnts[3].y));
148
                }
149
                else {//can only handle lines, quads, cubes
150
                    Base::Console().Error("Bad pole count (%d) for BezierSegment\n", geom->poles);
151
                    auto itBez = geom->pnts.begin() + 1;
152
                    for (; itBez != geom->pnts.end(); itBez++) {
153
                        path.lineTo(Rez::guiX((*itBez).x),
154
                                    Rez::guiX((*itBez).y));//show something for debugging
155
                    }
156
                }
157
            }
158
        } break;
159
        case TechDraw::BSPLINE: {
160
            TechDraw::BSplinePtr geom = std::static_pointer_cast<TechDraw::BSpline>(baseGeom);
161
            if (baseGeom->getReversed()) {
162
                // Move painter to the end of our last segment
163
                std::vector<TechDraw::BezierSegment>::const_reverse_iterator it =
164
                    geom->segments.rbegin();
165
                Base::Vector3d rStart = it->pnts.back();
166
                path.moveTo(Rez::guiX(rStart.x), Rez::guiX(rStart.y));
167

168
                for (; it != geom->segments.rend(); ++it) {
169
                    // At this point, the painter is either at the beginning
170
                    // of the first segment, or end of the last
171
                    if (it->poles == 2) {
172
                        // Degree 1 bezier = straight line...
173
                        path.lineTo(Rez::guiX(it->pnts[0].x), Rez::guiX(it->pnts[0].y));
174
                    }
175
                    else if (it->poles == 3) {
176
                        path.quadTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
177
                                    Rez::guiX(it->pnts[0].x), Rez::guiX(it->pnts[0].y));
178
                    }
179
                    else if (it->poles == 4) {
180
                        path.cubicTo(Rez::guiX(it->pnts[2].x), Rez::guiX(it->pnts[2].y),
181
                                     Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
182
                                     Rez::guiX(it->pnts[0].x), Rez::guiX(it->pnts[0].y));
183
                    }
184
                    else {//can only handle lines, quads, cubes
185
                        Base::Console().Error(
186
                            "Bad pole count (%d) for BezierSegment of B-spline geometry\n",
187
                            it->poles);
188
                        path.lineTo(it->pnts[1].x, it->pnts[1].y);//show something for debugging
189
                    }
190
                }
191
            }
192
            else {
193
                // Move painter to the beginning of our first segment
194
                std::vector<TechDraw::BezierSegment>::const_iterator it = geom->segments.begin();
195
                path.moveTo(Rez::guiX(it->pnts[0].x), Rez::guiX(it->pnts[0].y));
196

197
                for (; it != geom->segments.end(); ++it) {
198
                    // At this point, the painter is either at the beginning
199
                    // of the first segment, or end of the last
200
                    if (it->poles == 2) {
201
                        // Degree 1 bezier = straight line...
202
                        path.lineTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y));
203
                    }
204
                    else if (it->poles == 3) {
205
                        path.quadTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
206
                                    Rez::guiX(it->pnts[2].x), Rez::guiX(it->pnts[2].y));
207
                    }
208
                    else if (it->poles == 4) {
209
                        path.cubicTo(Rez::guiX(it->pnts[1].x), Rez::guiX(it->pnts[1].y),
210
                                     Rez::guiX(it->pnts[2].x), Rez::guiX(it->pnts[2].y),
211
                                     Rez::guiX(it->pnts[3].x), Rez::guiX(it->pnts[3].y));
212
                    }
213
                    else {
214
                        Base::Console().Error(
215
                            "Bad pole count (%d) for BezierSegment of B-spline geometry\n",
216
                            it->poles);
217
                        path.lineTo(it->pnts[1].x, it->pnts[1].y);//show something for debugging
218
                    }
219
                }
220
            }
221
        } break;
222
        case TechDraw::GENERIC: {
223
            TechDraw::GenericPtr geom = std::static_pointer_cast<TechDraw::Generic>(baseGeom);
224
            if (baseGeom->getReversed()) {
225
                if (!geom->points.empty()) {
226
                    Base::Vector3d rStart = geom->points.back();
227
                    path.moveTo(Rez::guiX(rStart.x), Rez::guiX(rStart.y));
228
                }
229
                std::vector<Base::Vector3d>::const_reverse_iterator it = geom->points.rbegin();
230
                for (++it; it != geom->points.rend(); ++it) {
231
                    path.lineTo(Rez::guiX((*it).x), Rez::guiX((*it).y));
232
                }
233
            }
234
            else {
235
                path.moveTo(Rez::guiX(geom->points[0].x), Rez::guiX(geom->points[0].y));
236
                std::vector<Base::Vector3d>::const_iterator it = geom->points.begin();
237
                for (++it; it != geom->points.end(); ++it) {
238
                    path.lineTo(Rez::guiX((*it).x), Rez::guiX((*it).y));
239
                }
240
            }
241
        } break;
242
        default: {
243
            Base::Console().Error("Error - geomToPainterPath - UNKNOWN geomType: %d\n",
244
                                  static_cast<int>(baseGeom->getGeomType()));
245
        } break;
246
    }//sb end of switch
247

248
    //old rotate path logic. now done on App side.
249
    //    if (rot != 0.0) {
250
    //        QTransform t;
251
    //        t.rotate(-rot);
252
    //        path = t.map(path);
253
    //    }
254

255
    return path;
256
}
257

258

259
// As called by arc of ellipse case:
260
// pathArc(path, geom->major, geom->minor, geom->angle, geom->largeArc, geom->cw,
261
//         geom->endPnt.x, geom->endPnt.y,
262
//         geom->startPnt.x, geom->startPnt.y);
263
void PathBuilder::pathArc(QPainterPath& path, double rx, double ry, double x_axis_rotation,
264
                          bool large_arc_flag, bool sweep_flag, double x, double y, double curx,
265
                          double cury) const
266
{
267
    double sin_th, cos_th;
268
    double a00, a01, a10, a11;
269
    double x0, y0, x1, y1, xc, yc;
270
    double d, sfactor, sfactor_sq;
271
    double th0, th1, th_arc;
272
    int i, n_segs;
273
    double dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
274

275
    rx = qAbs(rx);
276
    ry = qAbs(ry);
277

278
    sin_th = qSin(x_axis_rotation);
279
    cos_th = qCos(x_axis_rotation);
280

281
    dx = (curx - x) / 2.0;
282
    dy = (cury - y) / 2.0;
283
    dx1 = cos_th * dx + sin_th * dy;
284
    dy1 = -sin_th * dx + cos_th * dy;
285
    Pr1 = rx * rx;
286
    Pr2 = ry * ry;
287
    Px = dx1 * dx1;
288
    Py = dy1 * dy1;
289
    /* Spec : check if radii are large enough */
290
    check = Px / Pr1 + Py / Pr2;
291
    if (check > 1) {
292
        rx = rx * qSqrt(check);
293
        ry = ry * qSqrt(check);
294
    }
295

296
    a00 = cos_th / rx;
297
    a01 = sin_th / rx;
298
    a10 = -sin_th / ry;
299
    a11 = cos_th / ry;
300
    x0 = a00 * curx + a01 * cury;
301
    y0 = a10 * curx + a11 * cury;
302
    x1 = a00 * x + a01 * y;
303
    y1 = a10 * x + a11 * y;
304
    /* (x0, y0) is current point in transformed coordinate space.
305
       (x1, y1) is new point in transformed coordinate space.
306

307
       The arc fits a unit-radius circle in this space.
308
    */
309
    d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
310
    sfactor_sq = 1.0 / d - 0.25;
311
    if (sfactor_sq < 0)
312
        sfactor_sq = 0;
313

314
    sfactor = qSqrt(sfactor_sq);
315

316
    if (sweep_flag == large_arc_flag)
317
        sfactor = -sfactor;
318

319
    xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
320
    yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
321
    /* (xc, yc) is center of the circle. */
322

323
    th0 = qAtan2(y0 - yc, x0 - xc);
324
    th1 = qAtan2(y1 - yc, x1 - xc);
325

326
    th_arc = th1 - th0;
327
    if (th_arc < 0 && sweep_flag)
328
        th_arc += 2 * M_PI;
329
    else if (th_arc > 0 && !sweep_flag)
330
        th_arc -= 2 * M_PI;
331

332
    n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));
333

334
    path.moveTo(curx, cury);
335

336
    for (i = 0; i < n_segs; i++) {
337
        pathArcSegment(path, xc, yc, th0 + i * th_arc / n_segs, th0 + (i + 1) * th_arc / n_segs, rx,
338
                       ry, x_axis_rotation);
339
    }
340
}
341

342
void PathBuilder::pathArcSegment(QPainterPath& path, double xc, double yc, double th0, double th1,
343
                                 double rx, double ry, double xAxisRotation) const
344
{
345
    double sinTh, cosTh;
346
    double a00, a01, a10, a11;
347
    double x1, y1, x2, y2, x3, y3;
348
    double t;
349
    double thHalf;
350

351
    sinTh = qSin(xAxisRotation);
352
    cosTh = qCos(xAxisRotation);
353

354
    a00 = cosTh * rx;
355
    a01 = -sinTh * ry;
356
    a10 = sinTh * rx;
357
    a11 = cosTh * ry;
358

359
    thHalf = 0.5 * (th1 - th0);
360
    t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
361
    x1 = xc + qCos(th0) - t * qSin(th0);
362
    y1 = yc + qSin(th0) + t * qCos(th0);
363
    x3 = xc + qCos(th1);
364
    y3 = yc + qSin(th1);
365
    x2 = x3 + t * qSin(th1);
366
    y2 = y3 - t * qCos(th1);
367

368
    path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
369
                 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
370
}
371

372

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.