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