1 //------------------------------------------------------------------------------
2 // emPackLayout.cpp
3 //
4 // Copyright (C) 2015 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20
21 #include <emCore/emPackLayout.h>
22
23
emPackLayout(ParentArg parent,const emString & name,const emString & caption,const emString & description,const emImage & icon)24 emPackLayout::emPackLayout(
25 ParentArg parent, const emString & name, const emString & caption,
26 const emString & description, const emImage & icon
27 )
28 : emBorder(parent,name,caption,description,icon)
29 {
30 DefaultWeight=1.0;
31 DefaultPCT=0.2;
32 WeightArray.SetTuningLevel(4);
33 PCTArray.SetTuningLevel(4);
34 MinCellCount=0;
35 TI=NULL;
36 Ratings=0;
37 SetFocusable(false);
38 }
39
40
~emPackLayout()41 emPackLayout::~emPackLayout()
42 {
43 }
44
45
SetMinCellCount(int minCellCount)46 void emPackLayout::SetMinCellCount(int minCellCount)
47 {
48 if (minCellCount<0) minCellCount=0;
49 if (MinCellCount!=minCellCount) {
50 MinCellCount=minCellCount;
51 InvalidateChildrenLayout();
52 }
53 }
54
55
GetChildWeight(int index) const56 double emPackLayout::GetChildWeight(int index) const
57 {
58 if (index>=0 && index<WeightArray.GetCount()) {
59 return WeightArray[index];
60 }
61 else {
62 return DefaultWeight;
63 }
64 }
65
66
SetChildWeight(int index,double weight)67 void emPackLayout::SetChildWeight(int index, double weight)
68 {
69 int n;
70
71 if (index>=0) {
72 n=WeightArray.GetCount();
73 if (index<n) {
74 if (WeightArray[index]!=weight) {
75 WeightArray.Set(index,weight);
76 InvalidateChildrenLayout();
77 }
78 }
79 else {
80 if (DefaultWeight!=weight) {
81 if (index>n) WeightArray.Add(DefaultWeight,index-n);
82 WeightArray.Add(weight);
83 InvalidateChildrenLayout();
84 }
85 }
86 }
87 }
88
89
SetChildWeight(double weight)90 void emPackLayout::SetChildWeight(double weight)
91 {
92 if (DefaultWeight!=weight || !WeightArray.IsEmpty()) {
93 DefaultWeight=weight;
94 WeightArray.Clear();
95 InvalidateChildrenLayout();
96 }
97 }
98
99
GetPrefChildTallness(int index) const100 double emPackLayout::GetPrefChildTallness(int index) const
101 {
102 if (index>=0 && index<PCTArray.GetCount()) {
103 return PCTArray[index];
104 }
105 else {
106 return DefaultPCT;
107 }
108 }
109
110
SetPrefChildTallness(int index,double pct)111 void emPackLayout::SetPrefChildTallness(int index, double pct)
112 {
113 int n;
114
115 if (index>=0) {
116 n=PCTArray.GetCount();
117 if (index<n) {
118 if (PCTArray[index]!=pct) {
119 PCTArray.Set(index,pct);
120 InvalidateChildrenLayout();
121 }
122 }
123 else {
124 if (DefaultPCT!=pct) {
125 if (index>n) PCTArray.Add(DefaultPCT,index-n);
126 PCTArray.Add(pct);
127 InvalidateChildrenLayout();
128 }
129 }
130 }
131 }
132
133
SetPrefChildTallness(double pct)134 void emPackLayout::SetPrefChildTallness(double pct)
135 {
136 if (DefaultPCT!=pct || !PCTArray.IsEmpty()) {
137 DefaultPCT=pct;
138 PCTArray.Clear();
139 InvalidateChildrenLayout();
140 }
141 }
142
143
LayoutChildren()144 void emPackLayout::LayoutChildren()
145 {
146 TmpPanelInfo autoMem[64];
147 TmpPanelInfo * dynMem;
148 TmpInfo ti;
149 double x,y,w,h;
150 int cells,tpiCnt;
151 emColor cc;
152
153 emBorder::LayoutChildren();
154
155 cells=CountCells();
156 if (cells>0) {
157 TI=&ti;
158
159 tpiCnt=cells+1;
160 if (tpiCnt*sizeof(TmpPanelInfo)<=sizeof(autoMem)) {
161 dynMem=NULL;
162 TI->TPIs=autoMem;
163 }
164 else {
165 dynMem=(TmpPanelInfo*)malloc(tpiCnt*sizeof(TmpPanelInfo));
166 TI->TPIs=dynMem;
167 }
168 FillTPIs(tpiCnt);
169
170 GetContentRectUnobscured(&x,&y,&w,&h,&TI->CanvasColor);
171 if (w<1E-100) w=1E-100;
172 if (h<1E-100) h=1E-100;
173
174 Ratings=0;
175
176 PackN(0, cells, x, y, w, h, 1E100, true);
177
178 emDLog(
179 "emPackLayout::LayoutChildren: cells = %d, ratings = %d, ratings/cell = %g",
180 cells, Ratings, cells>0 ? ((double)Ratings)/cells : 0.0
181 );
182
183 TI=NULL;
184 if (dynMem) free(dynMem);
185 }
186 }
187
188
CountCells()189 int emPackLayout::CountCells()
190 {
191 emPanel * p, * aux;
192 int cells;
193
194 aux=GetAuxPanel();
195 for (cells=0, p=GetFirstChild(); p; p=p->GetNext()) {
196 if (p!=aux) cells++;
197 }
198 if (cells<MinCellCount) cells=MinCellCount;
199 return cells;
200 }
201
202
FillTPIs(int count)203 void emPackLayout::FillTPIs(int count)
204 {
205 double pct,cumulativeWeight,cumulativeLogPCT;
206 emPanel * p, * aux;
207 int i;
208
209 pct=1.0;
210 cumulativeWeight=0.0;
211 cumulativeLogPCT=0.0;
212 for (i=0; i<count; i++) {
213 if (i>0) {
214 cumulativeWeight+=GetChildWeight(i-1);
215 cumulativeLogPCT+=log(pct);
216 }
217 pct=emMax(1E-4,GetPrefChildTallness(i));
218 TI->TPIs[i].PCT=pct;
219 TI->TPIs[i].CumulativeWeight=cumulativeWeight;
220 TI->TPIs[i].CumulativeLogPCT=cumulativeLogPCT;
221 TI->TPIs[i].Panel=NULL;
222 }
223
224 aux=GetAuxPanel();
225 for (i=0, p=GetFirstChild(); p && i<count; p=p->GetNext()) {
226 if (p!=aux) TI->TPIs[i++].Panel=p;
227 }
228 }
229
230
GetTPIWeightSum(int index,int count) const231 double emPackLayout::GetTPIWeightSum(int index, int count) const
232 {
233 return
234 TI->TPIs[index+count].CumulativeWeight -
235 TI->TPIs[index].CumulativeWeight
236 ;
237 }
238
239
GetTPILogPCTSum(int index,int count) const240 double emPackLayout::GetTPILogPCTSum(int index, int count) const
241 {
242 return (
243 TI->TPIs[index+count].CumulativeLogPCT -
244 TI->TPIs[index].CumulativeLogPCT
245 );
246 }
247
248
RateCell(int index,double w,double h)249 double emPackLayout::RateCell(int index, double w, double h)
250 {
251 double error;
252
253 Ratings++;
254
255 error=w/h*TI->TPIs[index].PCT;
256 if (error<1.0) error=1.0/error;
257 error=pow(error,3.0)-1.0;
258 return error;
259 }
260
261
Pack1(int index,double x,double y,double w,double h,bool execute)262 double emPackLayout::Pack1(
263 int index,
264 double x, double y, double w, double h,
265 bool execute
266 )
267 {
268 emPanel * p;
269
270 if (execute) {
271 p=TI->TPIs[index].Panel;
272 if (p) p->Layout(x,y,w,h,TI->CanvasColor);
273 }
274 return RateCell(index,w,h);
275 }
276
277
Pack2(int index,double x,double y,double w,double h,double bestError,bool execute)278 double emPackLayout::Pack2(
279 int index,
280 double x, double y, double w, double h,
281 double bestError, bool execute
282 )
283 {
284 double s1,w1,h1,error;
285 emPanel * p;
286 int solution;
287
288 s1=GetTPIWeightSum(index,1)/GetTPIWeightSum(index,2);
289 w1=w*s1;
290 h1=h*s1;
291
292 solution=-1;
293
294 error=RateCell(index,w1,h);
295 if (error<bestError) {
296 error+=RateCell(index+1,w-w1,h);
297 if (error<bestError) {
298 bestError=error;
299 solution=0;
300 }
301 }
302 error=RateCell(index,w,h1);
303 if (error<bestError) {
304 error+=RateCell(index+1,w,h-h1);
305 if (error<bestError) {
306 bestError=error;
307 solution=1;
308 }
309 }
310
311 if (execute) {
312 switch (solution) {
313 case 0:
314 p=TI->TPIs[index].Panel;
315 if (p) p->Layout(x,y,w1,h,TI->CanvasColor);
316 p=TI->TPIs[index+1].Panel;
317 if (p) p->Layout(x+w1,y,w-w1,h,TI->CanvasColor);
318 break;
319 default:
320 p=TI->TPIs[index].Panel;
321 if (p) p->Layout(x,y,w,h1,TI->CanvasColor);
322 p=TI->TPIs[index+1].Panel;
323 if (p) p->Layout(x,y+h1,w,h-h1,TI->CanvasColor);
324 break;
325 }
326 }
327
328 return solution>=0 ? bestError : 1E100;
329 }
330
331
Pack3(int index,double x,double y,double w,double h,double bestError,bool execute)332 double emPackLayout::Pack3(
333 int index,
334 double x, double y, double w, double h,
335 double bestError, bool execute
336 )
337 {
338 double f,a1,a2,a3,w1,w2,w3,w12,w23,h1,h2,h3,h12,h23,error,e1,e3;
339 emPanel * p;
340 int solution;
341
342 f=w*h/GetTPIWeightSum(index,3);
343 a1=GetTPIWeightSum(index,1)*f;
344 a2=GetTPIWeightSum(index+1,1)*f;
345 a3=GetTPIWeightSum(index+2,1)*f;
346
347 solution=-1;
348
349 w1=a1/h;
350 w2=a2/h;
351 w3=a3/h;
352 w12=w1+w2;
353 w23=w2+w3;
354 e1=RateCell(index,w1,h);
355 e3=RateCell(index+2,w3,h);
356 if (e1<bestError) {
357 error=e1+e3;
358 if (error<bestError) {
359 error+=RateCell(index+1,w2,h);
360 if (error<bestError) {
361 bestError=error;
362 solution=0;
363 }
364 }
365 error=e1+RateCell(index+1,w23,a2/w23);
366 if (error<bestError) {
367 error+=RateCell(index+2,w23,a3/w23);
368 if (error<bestError) {
369 bestError=error;
370 solution=1;
371 }
372 }
373 }
374 if (e3<bestError) {
375 error=e3+RateCell(index,w12,a1/w12);
376 if (error<bestError) {
377 error+=RateCell(index+1,w12,a2/w12);
378 if (error<bestError) {
379 bestError=error;
380 solution=2;
381 }
382 }
383 }
384
385 h1=a1/w;
386 h2=a2/w;
387 h3=a3/w;
388 h12=h1+h2;
389 h23=h2+h3;
390 e1=RateCell(index,w,h1);
391 e3=RateCell(index+2,w,h3);
392 if (e1<bestError) {
393 error=e1+e3;
394 if (error<bestError) {
395 error+=RateCell(index+1,w,h2);
396 if (error<bestError) {
397 bestError=error;
398 solution=3;
399 }
400 }
401 error=e1+RateCell(index+1,a2/h23,h23);
402 if (error<bestError) {
403 error+=RateCell(index+2,a3/h23,h23);
404 if (error<bestError) {
405 bestError=error;
406 solution=4;
407 }
408 }
409 }
410 if (e3<bestError) {
411 error=e3+RateCell(index,a1/h12,h12);
412 if (error<bestError) {
413 error+=RateCell(index+1,a2/h12,h12);
414 if (error<bestError) {
415 bestError=error;
416 solution=5;
417 }
418 }
419 }
420
421 if (execute) {
422 switch (solution) {
423 case 0:
424 p=TI->TPIs[index].Panel;
425 if (p) p->Layout(x,y,w1,h,TI->CanvasColor);
426 p=TI->TPIs[index+1].Panel;
427 if (p) p->Layout(x+w1,y,w2,h,TI->CanvasColor);
428 p=TI->TPIs[index+2].Panel;
429 if (p) p->Layout(x+w12,y,w3,h,TI->CanvasColor);
430 break;
431 case 1:
432 p=TI->TPIs[index].Panel;
433 if (p) p->Layout(x,y,w1,h,TI->CanvasColor);
434 p=TI->TPIs[index+1].Panel;
435 if (p) p->Layout(x+w1,y,w23,a2/w23,TI->CanvasColor);
436 p=TI->TPIs[index+2].Panel;
437 if (p) p->Layout(x+w1,y+a2/w23,w23,a3/w23,TI->CanvasColor);
438 break;
439 case 2:
440 p=TI->TPIs[index].Panel;
441 if (p) p->Layout(x,y,w12,a1/w12,TI->CanvasColor);
442 p=TI->TPIs[index+1].Panel;
443 if (p) p->Layout(x,y+a1/w12,w12,a2/w12,TI->CanvasColor);
444 p=TI->TPIs[index+2].Panel;
445 if (p) p->Layout(x+w12,y,w3,h,TI->CanvasColor);
446 break;
447 case 3:
448 p=TI->TPIs[index].Panel;
449 if (p) p->Layout(x,y,w,h1,TI->CanvasColor);
450 p=TI->TPIs[index+1].Panel;
451 if (p) p->Layout(x,y+h1,w,h2,TI->CanvasColor);
452 p=TI->TPIs[index+2].Panel;
453 if (p) p->Layout(x,y+h12,w,h3,TI->CanvasColor);
454 break;
455 case 4:
456 p=TI->TPIs[index].Panel;
457 if (p) p->Layout(x,y,w,h1,TI->CanvasColor);
458 p=TI->TPIs[index+1].Panel;
459 if (p) p->Layout(x,y+h1,a2/h23,h23,TI->CanvasColor);
460 p=TI->TPIs[index+2].Panel;
461 if (p) p->Layout(x+a2/h23,y+h1,a3/h23,h23,TI->CanvasColor);
462 break;
463 default:
464 p=TI->TPIs[index].Panel;
465 if (p) p->Layout(x,y,a1/h12,h12,TI->CanvasColor);
466 p=TI->TPIs[index+1].Panel;
467 if (p) p->Layout(x+a1/h12,y,a2/h12,h12,TI->CanvasColor);
468 p=TI->TPIs[index+2].Panel;
469 if (p) p->Layout(x,y+h12,w,h3,TI->CanvasColor);
470 break;
471 }
472 }
473
474 return solution>=0 ? bestError : 1E100;
475 }
476
477
PackN(int index,int count,double x,double y,double w,double h,double bestError,bool execute)478 double emPackLayout::PackN(
479 int index, int count,
480 double x, double y, double w, double h,
481 double bestError, bool execute
482 )
483 {
484 double error,totalWeight,s1,w1,h1;
485 int i,n,div,bestDiv;
486 bool bestHorizontal,testHorizontalFirst;
487
488 if (count==1) {
489 return Pack1(index,x,y,w,h,execute);
490 }
491 if (count==2) {
492 return Pack2(index,x,y,w,h,bestError,execute);
493 }
494 if (count==3) {
495 return Pack3(index,x,y,w,h,bestError,execute);
496 }
497
498 totalWeight=GetTPIWeightSum(index,count);
499 testHorizontalFirst=(log(h/w) < GetTPILogPCTSum(index,count)/count);
500
501 if (count <= 7) n=(count-1)*2;
502 else if (count == 8) n=11;
503 else if (count == 9) n=8;
504 else if (count == 10) n=6;
505 else if (count == 11) n=4;
506 else if (count <= 15) n=3;
507 else if (count <= 20) n=2;
508 else n=1;
509
510 if (n<=1) {
511 bestDiv=count>>1;
512 bestHorizontal=testHorizontalFirst;
513 bestError=1E100;
514 }
515 else {
516 bestDiv=-1;
517 bestHorizontal=testHorizontalFirst;
518 for (i=0; i<n; i+=2) {
519 if (i&2) div=(count+(i>>1)+1)>>1;
520 else div=(count-(i>>1))>>1;
521 s1=GetTPIWeightSum(index,div)/totalWeight;
522 h1=h*s1;
523 w1=w*s1;
524 if (testHorizontalFirst) {
525 error=RateHorizontally(index,count,div,x,y,w1,w-w1,h,bestError);
526 if (error<bestError) {
527 bestError=error;
528 bestDiv=div;
529 bestHorizontal=true;
530 }
531 if (i+1==n) break;
532 error=RateVertically(index,count,div,x,y,w,h1,h-h1,bestError);
533 if (error<bestError) {
534 bestError=error;
535 bestDiv=div;
536 bestHorizontal=false;
537 }
538 }
539 else {
540 error=RateVertically(index,count,div,x,y,w,h1,h-h1,bestError);
541 if (error<bestError) {
542 bestError=error;
543 bestDiv=div;
544 bestHorizontal=false;
545 }
546 if (i+1==n) break;
547 error=RateHorizontally(index,count,div,x,y,w1,w-w1,h,bestError);
548 if (error<bestError) {
549 bestError=error;
550 bestDiv=div;
551 bestHorizontal=true;
552 }
553 }
554 }
555 }
556
557 if (execute) {
558 div=bestDiv;
559 if (div<0) div=count>>1;
560 s1=GetTPIWeightSum(index,div)/totalWeight;
561 bestError=bestError*1.00000001+1E-100;
562 if (!bestHorizontal) {
563 h1=h*s1;
564 PackN(
565 index, div,
566 x, y, w, h1,
567 bestError, true
568 );
569 PackN(
570 index+div, count-div,
571 x, y+h1, w, h-h1,
572 bestError, true
573 );
574 }
575 else {
576 w1=w*s1;
577 PackN(
578 index, div,
579 x, y, w1, h,
580 bestError, true
581 );
582 PackN(
583 index+div, count-div,
584 x+w1, y, w-w1, h,
585 bestError, true
586 );
587 }
588 }
589
590 return bestDiv>=0 ? bestError : 1E100;
591 }
592
593
RateHorizontally(int index,int count,int div,double x,double y,double w1,double w2,double h,double bestError)594 double emPackLayout::RateHorizontally(
595 int index, int count, int div,
596 double x, double y, double w1, double w2, double h,
597 double bestError
598 )
599 {
600 double error;
601
602 if (div<=count/2) {
603 error=PackN(
604 index,div,x,y,w1,h,bestError,false
605 );
606 if (error<bestError) {
607 error+=PackN(
608 index+div,count-div,x+w1,y,w2,h,bestError,false
609 );
610 }
611 }
612 else {
613 error=PackN(
614 index+div,count-div,x+w1,y,w2,h,bestError,false
615 );
616 if (error<bestError) {
617 error+=PackN(
618 index,div,x,y,w1,h,bestError,false
619 );
620 }
621 }
622
623 return error;
624 }
625
626
RateVertically(int index,int count,int div,double x,double y,double w,double h1,double h2,double bestError)627 double emPackLayout::RateVertically(
628 int index, int count, int div,
629 double x, double y, double w, double h1, double h2,
630 double bestError
631 )
632 {
633 double error;
634
635 if (div<=count/2) {
636 error=PackN(
637 index,div,x,y,w,h1,bestError,false
638 );
639 if (error<bestError) {
640 error+=PackN(
641 index+div,count-div,x,y+h1,w,h2,bestError,false
642 );
643 }
644 }
645 else{
646 error=PackN(
647 index+div,count-div,x,y+h1,w,h2,bestError,false
648 );
649 if (error<bestError) {
650 error+=PackN(
651 index,div,x,y,w,h1,bestError,false
652 );
653 }
654 }
655
656 return error;
657 }
658