/
/
/
1#include "q_pen_drawer/bezierline.h"
2
3#include <algorithm>
4#include <cmath>
5#include <QtMath>
6#include <iostream>
7#include <QTransform>
8
9BezierLine::BezierLine(){
10}
11
12QRectF BezierLine::boundingRect() const {
13 if(points_.begin() == points_.end()){
14 return QRectF(0.0, 0.0, 0.0, 0.0);
15 }
16 else{
17 auto x_min = std::min_element(points_.begin(), points_.end(), [](const BezierPoint &a, const BezierPoint &b){
18 return a.position.x() < b.position.x();
19 })->position.x();
20 auto x_max = std::max_element(points_.begin(), points_.end(), [](const BezierPoint &a, const BezierPoint &b){
21 return a.position.x() > b.position.x();
22 })->position.x();
23 auto y_min = std::min_element(points_.begin(), points_.end(), [](const BezierPoint &a, const BezierPoint &b){
24 return a.position.y() < b.position.y();
25 })->position.y();
26 auto y_max = std::max_element(points_.begin(), points_.end(), [](const BezierPoint &a, const BezierPoint &b){
27 return a.position.y() > b.position.y();
28 })->position.y();
29 return QRectF(x_min-5, y_min-5, x_max - x_min + 5, y_max - y_min + 5);
30 }
31}
32
33bool BezierLine::selectClosest(QPointF point){
34 for(int i=0 ; i<points_.size(); i++){
35 if(std::hypot((points_[i].position - point).x(), (points_[i].position - point).y()) < 5){
36 active_point_ = qMakePair(i, UpdateMode::Position);
37 return true;
38 }
39 else if(std::hypot((points_[i].control_before - point).x(), (points_[i].control_before - point).y()) < 5){
40 active_point_ = qMakePair(i, UpdateMode::ControlBefore);
41 return true;
42 }
43 else if(std::hypot((points_[i].control_after - point).x(), (points_[i].control_after - point).y()) < 5){
44 active_point_ = qMakePair(i, UpdateMode::ControlAfter);
45 return true;
46 }
47 }
48 active_point_ = qMakePair(-1, UpdateMode::ControlAfter);
49 return false;
50}
51
52void BezierLine::addPoint(QPointF point){
53 points_.push_back({point, point, point});
54 active_point_ = qMakePair(-1, UpdateMode::ControlAfter);
55}
56
57void BezierLine::updateControlPoint(QPointF point){
58 if(active_point_.first == -1){
59 auto current_point_iterator = points_.end()-1;
60 current_point_iterator->control_after = point;
61 current_point_iterator->control_before = 2 * current_point_iterator->position - point;
62 }
63 else{
64 switch (active_point_.second) {
65 case UpdateMode::Position: {
66 QPointF delta = point - points_[active_point_.first].position;
67 points_[active_point_.first].position += delta;
68 points_[active_point_.first].control_after += delta;
69 points_[active_point_.first].control_before += delta;
70 break;
71 }
72 case UpdateMode::ControlAfter: {
73 QPointF w = point - points_[active_point_.first].position;
74 QPointF v = points_[active_point_.first].control_after - points_[active_point_.first].position;
75
76 points_[active_point_.first].control_after = point;
77 qreal delta = qAtan2(w.y() * v.x() - w.x() * v.y(), w.x() * v.x() + w.y() * v.y());
78
79 qreal cosa = std::cos(delta);
80 qreal sina = std::sin(delta);
81
82 QTransform translate(1, 0, 0, 1, -points_[active_point_.first].position.x(), -points_[active_point_.first].position.y());
83 QTransform rotate(cosa, sina, -sina, cosa, 0, 0);
84 QTransform back_translate(1, 0, 0, 1, points_[active_point_.first].position.x(), points_[active_point_.first].position.y());
85
86 QTransform transform = translate * rotate * back_translate;
87
88 points_[active_point_.first].control_before = transform.map(points_[active_point_.first].control_before);
89
90 break;
91 }
92 case UpdateMode::ControlBefore: {
93 QPointF w = point - points_[active_point_.first].position;
94 QPointF v = points_[active_point_.first].control_before - points_[active_point_.first].position;
95
96 points_[active_point_.first].control_before = point;
97 qreal delta = qAtan2(w.y() * v.x() - w.x() * v.y(), w.x() * v.x() + w.y() * v.y());
98
99 qreal cosa = std::cos(delta);
100 qreal sina = std::sin(delta);
101
102 QTransform translate(1, 0, 0, 1, -points_[active_point_.first].position.x(), -points_[active_point_.first].position.y());
103 QTransform rotate(cosa, sina, -sina, cosa, 0, 0);
104 QTransform back_translate(1, 0, 0, 1, points_[active_point_.first].position.x(), points_[active_point_.first].position.y());
105
106 QTransform transform = translate * rotate * back_translate;
107
108 points_[active_point_.first].control_after = transform.map(points_[active_point_.first].control_after);
109
110 break;
111 }
112 }
113
114 }
115}
116
117QList<QPointF> BezierLine::getPoints(){
118 return QList<QPointF>();
119}
120
121void BezierLine::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/){
122 QPen position_pen, control_pen, line_pen;
123
124 position_pen.setWidth(2);
125 position_pen.setStyle(Qt::SolidLine);
126 control_pen = position_pen;
127
128 line_pen.setWidth(1);
129 line_pen.setStyle(Qt::DotLine);
130 line_pen.setColor(Qt::blue);
131
132 position_pen.setColor(Qt::green);
133 control_pen.setColor(Qt::blue);
134
135
136 for(const auto &point : points_){
137 painter->setPen(position_pen);
138 painter->drawEllipse(point.position.x() - point_size_ / 2, point.position.y() - point_size_ / 2, point_size_, point_size_);
139 painter->setPen(control_pen);
140 painter->drawEllipse(point.control_before.x() - point_size_ / 2, point.control_before.y() - point_size_ / 2, point_size_, point_size_);
141 painter->drawEllipse(point.control_after.x() - point_size_ / 2, point.control_after.y() - point_size_ / 2, point_size_, point_size_);
142 painter->setPen(line_pen);
143 painter->drawLine(point.control_before, point.control_after);
144 }
145
146 if(points_.begin() == points_.end()) return;
147
148 if(points_.begin() + 1 == points_.end()) return;
149
150 for(auto it = points_.begin() ; it != points_.end(); it++){
151 if (it == points_.begin()) continue;
152
153 auto arc_length_approx = (std::hypot(((it-1)->position - it->position).x(), ((it-1)->position - it->position).y() ) +
154 std::hypot(((it-1)->position - (it-1)->control_after).x(), ((it-1)->position - (it-1)->control_after).y() ) +
155 std::hypot((it->control_before - (it-1)->control_after).x(), (it->control_before - (it-1)->control_after).y() ) +
156 std::hypot((it->position - it->control_before).x(), (it->position - it->control_before).y() )) / 2.0;
157
158 qreal delta = average_step_length_ / arc_length_approx;
159
160 qreal t = delta;
161
162 QPointF last = (it-1)->position;
163
164 while(t < 1.0){
165
166 QPointF p_b = qPow(1.0-t, 3) * (it-1)->position +
167 3 * qPow(1.0 - t, 2) * t * (it-1)->control_after +
168 3 * qPow(t, 2) * (1.0 - t) * it->control_before +
169 qPow(t, 3) * it->position;
170
171 painter->setPen(control_pen);
172 painter->drawEllipse(p_b.x() - 1, p_b.y() - 1, 2, 2);
173 painter->drawLine(last, p_b);
174 last = p_b;
175 t += delta;
176 }
177
178 painter->drawLine(last, it->position);
179
180 }
181}
182