1 /***************************************************************************
2 * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3 * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 ***************************************************************************/
6 /** @file
7 * This file implements classes SKGTreeMap.
8 *
9 * @author Stephane MANKOWSKI
10 */
11 #include "skgtreemap.h"
12
13 #include "skgtraces.h"
14
SKGTreeMap(QString iID,double iValue,double iX,double iY,double iW,double iH)15 SKGTreeMap::SKGTreeMap(QString iID,
16 double iValue,
17 double iX,
18 double iY,
19 double iW,
20 double iH)
21 : m_id(std::move(iID)), m_value(iValue), m_x(iX), m_y(iY), m_w(iW), m_h(iH)
22 {}
23
24 SKGTreeMap::~SKGTreeMap() = default;
25
getID() const26 QString SKGTreeMap::getID() const
27 {
28 return m_id;
29 }
30
getValue() const31 double SKGTreeMap::getValue() const
32 {
33 return m_value;
34 }
35
setX(double iX)36 void SKGTreeMap::setX(double iX)
37 {
38 m_x = iX;
39 }
40
getX() const41 double SKGTreeMap::getX() const
42 {
43 return m_x;
44 }
45
setY(double iY)46 void SKGTreeMap::setY(double iY)
47 {
48 m_y = iY;
49 }
50
getY() const51 double SKGTreeMap::getY() const
52 {
53 return m_y;
54 }
55
setW(double iW)56 void SKGTreeMap::setW(double iW)
57 {
58 m_w = iW;
59 }
60
getW() const61 double SKGTreeMap::getW() const
62 {
63 return m_w;
64 }
65
setH(double iH)66 void SKGTreeMap::setH(double iH)
67 {
68 m_h = iH;
69 }
70
getH() const71 double SKGTreeMap::getH() const
72 {
73 return m_h;
74 }
75
addChild(const SKGTreeMap & iChildren)76 void SKGTreeMap::addChild(const SKGTreeMap& iChildren)
77 {
78 m_children.append(iChildren);
79 }
80
getChildren() const81 QList<SKGTreeMap> SKGTreeMap::getChildren() const
82 {
83 return m_children;
84 }
85
computeValuesAndSort()86 void SKGTreeMap::computeValuesAndSort()
87 {
88 // Compute the value
89 if (m_children.count() != 0) {
90 // Compute the value
91 double sum = 0.0;
92 for (auto& item : m_children) {
93 item.computeValuesAndSort();
94 sum += item.getValue();
95 }
96
97 // Set sum on tile
98 m_value = sum;
99
100 // Sort the children by abs(value) desc
101 std::sort(m_children.begin(), m_children.end(), [](const SKGTreeMap & a, const SKGTreeMap & b) -> bool {
102 if (qAbs(a.getValue() - b.getValue()) < 10e-5)
103 {
104 return a.getID() < b.getID();
105 }
106 return a.getValue() > b.getValue();
107 });
108 }
109 }
110
getAllTilesById() const111 QMap<QString, SKGTreeMap> SKGTreeMap::getAllTilesById() const
112 {
113 QMap<QString, SKGTreeMap> output;
114 output.insert(getID(), *this);
115 for (const auto& item : qAsConst(m_children)) {
116 output.insert(item.getID(), item);
117 auto children = item.getAllTilesById();
118 for (const auto& item2 : qAsConst(children)) {
119 output.insert(item2.getID(), item2);
120 }
121 }
122 return output;
123 }
124
compute()125 void SKGTreeMap::compute()
126 {
127 // Prepare all the structure
128 computeValuesAndSort();
129
130 // If value = 0
131 bool isValueZero = (getValue() < 10e-5);
132
133 // Start the layout
134 int nb = m_children.count();
135 if (nb == 0) {
136 // Nothing to do
137 } else if (nb == 1) {
138 // The child item must take the full place
139 m_children[0].setX(getX());
140 m_children[0].setY(getY());
141 m_children[0].setW(getW());
142 m_children[0].setH(getH());
143
144 m_children[0].compute();
145 } else if (nb == 2) {
146 if (getW() >= getH()) {
147 // Horizontal mode
148 // Set the first
149 m_children[0].setX(getX());
150 m_children[0].setY(getY());
151 m_children[0].setW(isValueZero ? 0.0 : getW()*m_children.at(0).getValue() / getValue());
152 m_children[0].setH(getH());
153
154 m_children[0].compute();
155
156 // Set the second
157 m_children[1].setX(getX() + m_children.at(0).getW());
158 m_children[1].setY(getY());
159 m_children[1].setW(getW() - m_children.at(0).getW());
160 m_children[1].setH(getH());
161
162 m_children[1].compute();
163 } else {
164 // Vertical
165 // Set the first
166 m_children[0].setX(getX());
167 m_children[0].setY(getY());
168 m_children[0].setW(getW());
169 m_children[0].setH(isValueZero ? 0.0 : getH()*m_children.at(0).getValue() / getValue());
170
171 m_children[0].compute();
172
173 // Set the second
174 m_children[1].setX(getX());
175 m_children[1].setY(getY() + m_children.at(0).getH());
176 m_children[1].setW(getW());
177 m_children[1].setH(getH() - m_children.at(0).getH());
178
179 m_children[1].compute();
180 }
181 } else {
182 // Compute the number of element that can be aligned
183 double sum = 0.0;
184 int optimum = 0;
185 double lastratio = 1000.0;
186 double previous_gw = 0.0;
187 double previous_gh = 0.0;
188 for (int i = 0; i < nb; ++i) {
189 sum += m_children.at(i).getValue();
190 if (getW() >= getH()) {
191 double gw = isValueZero ? 0.0 : getW() * sum / getValue();
192 double ih = isValueZero ? 0.0 : m_children.at(i).getValue() * getW() * getH() / (gw * getValue());
193 double ratio = qMax(ih / gw, gw / ih);
194 if (ratio > lastratio) {
195 // This ratio is worst than te previous one
196 sum -= m_children.at(i).getValue();
197 optimum = i - 1;
198 break;
199 }
200 lastratio = ratio;
201 previous_gw = gw;
202 } else {
203 double gh = isValueZero ? 0.0 : getH() * sum / getValue();
204 double iw = isValueZero ? 0.0 : m_children.at(i).getValue() * getW() * getH() / (gh * getValue());
205 double ratio = qMax(gh / iw, iw / gh);
206 if (ratio > lastratio) {
207 // This ratio is worst than te previous one
208 sum -= m_children.at(i).getValue();
209 optimum = i - 1;
210 break;
211 }
212 lastratio = ratio;
213 previous_gh = gh;
214 }
215 }
216
217 // Set the layout
218 double current_xy = 0.0;
219 for (int i = 0; i <= optimum; ++i) {
220 if (getW() >= getH()) {
221 double ih = isValueZero ? 0.0 : m_children.at(i).getValue() * getW() * getH() / (previous_gw * getValue());
222
223 m_children[i].setX(getX());
224 m_children[i].setY(getY() + current_xy);
225 m_children[i].setW(previous_gw);
226 m_children[i].setH(ih);
227 current_xy += ih;
228
229 m_children[i].compute();
230 } else {
231 double iw = isValueZero ? 0.0 : m_children.at(i).getValue() * getW() * getH() / (previous_gh * getValue());
232
233 m_children[i].setX(getX() + current_xy);
234 m_children[i].setY(getY());
235 m_children[i].setW(iw);
236 m_children[i].setH(previous_gh);
237 current_xy += iw;
238
239 m_children[i].compute();
240 }
241 }
242
243 // Treat the rest
244 if (optimum == -1) {
245 optimum = 1;
246 }
247 if (optimum < nb - 1) {
248 // Create a new SKGTreeMap corresponding to the rest
249 double x = getW() >= getH() ? getX() + previous_gw : getX();
250 double y = getW() >= getH() ? getY() : getY() + previous_gh;
251 double w = getW() >= getH() ? getW() - previous_gw : getW();
252 double h = getW() >= getH() ? getH() : getH() - previous_gh;
253 SKGTreeMap rest(QLatin1String(""), getValue() - sum, x, y, w, h);
254
255 // Add all items to compute
256 for (int i = optimum + 1; i < nb; ++i) {
257 rest.addChild(m_children.at(i));
258 }
259
260 // Compute
261 rest.compute();
262 auto computed = rest.getChildren();
263 for (int i = optimum + 1; i < nb; ++i) {
264 m_children[i] = computed[i - optimum - 1];
265 }
266 }
267 }
268 }
269