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