1 //------------------------------------------------------------------------------
2 // emViewAnimator.cpp
3 //
4 // Copyright (C) 2014-2017 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/emViewAnimator.h>
22 #include <emCore/emPanel.h>
23 
24 
25 //==============================================================================
26 //=============================== emViewAnimator ===============================
27 //==============================================================================
28 
emViewAnimator(emView & view)29 emViewAnimator::emViewAnimator(emView & view)
30 	: emEngine(view.GetScheduler()),
31 	View(view)
32 {
33 	Master=NULL;
34 	ActiveSlave=NULL;
35 	UpperActivePtr=&View.ActiveAnimator;
36 	LastTSC=0;
37 	LastClk=0;
38 	DeactivateWhenIdle=false;
39 	SetEnginePriority(emEngine::HIGH_PRIORITY);
40 }
41 
42 
~emViewAnimator()43 emViewAnimator::~emViewAnimator()
44 {
45 	Deactivate();
46 }
47 
48 
SetMaster(emViewAnimator * master)49 void emViewAnimator::SetMaster(emViewAnimator * master)
50 {
51 	emViewAnimator * va;
52 
53 	if (Master!=master) {
54 		if (IsActive()) Deactivate();
55 		if (Master) {
56 			Master=NULL;
57 			UpperActivePtr=&View.ActiveAnimator;
58 		}
59 		if (master) {
60 			for (va=master; va; va=va->Master) if (va==this) return;
61 			Master=master;
62 			UpperActivePtr=&Master->ActiveSlave;
63 		}
64 	}
65 }
66 
67 
Activate()68 void emViewAnimator::Activate()
69 {
70 	if (!IsActive() && (!Master || Master->IsActive())) {
71 		if (*UpperActivePtr) {
72 			LastTSC=(*UpperActivePtr)->LastTSC;
73 			LastClk=(*UpperActivePtr)->LastClk;
74 			(*UpperActivePtr)->Deactivate();
75 		}
76 		else if (Master) {
77 			LastTSC=Master->LastTSC;
78 			LastClk=Master->LastClk;
79 		}
80 		*UpperActivePtr=this;
81 		WakeUp();
82 		emDLog("emViewAnimator::Activate: class = %s",typeid(*this).name());
83 	}
84 }
85 
86 
Deactivate()87 void emViewAnimator::Deactivate()
88 {
89 	if (ActiveSlave) {
90 		ActiveSlave->Deactivate();
91 	}
92 
93 	if (*UpperActivePtr==this) {
94 		*UpperActivePtr=NULL;
95 		emDLog("emViewAnimator::Deactivate: class = %s",typeid(*this).name());
96 	}
97 }
98 
99 
SetDeactivateWhenIdle(bool deactivateWhenIdle)100 void emViewAnimator::SetDeactivateWhenIdle(bool deactivateWhenIdle)
101 {
102 	if (DeactivateWhenIdle!=deactivateWhenIdle) {
103 		DeactivateWhenIdle=deactivateWhenIdle;
104 		if (DeactivateWhenIdle && IsActive()) {
105 			WakeUp(); // To be sure to deactivate soon if already idle.
106 		}
107 	}
108 }
109 
110 
Input(emInputEvent & event,const emInputState & state)111 void emViewAnimator::Input(emInputEvent & event, const emInputState & state)
112 {
113 	if (ActiveSlave) ActiveSlave->Input(event,state);
114 }
115 
116 
Paint(const emPainter & painter) const117 void emViewAnimator::Paint(const emPainter & painter) const
118 {
119 	if (ActiveSlave) ActiveSlave->Paint(painter);
120 }
121 
122 
Cycle()123 bool emViewAnimator::Cycle()
124 {
125 	emUInt64 clk,tsc;
126 	double dt;
127 	bool busy;
128 
129 	if (IsActive()) {
130 		tsc=GetScheduler().GetTimeSliceCounter();
131 		if (tsc!=LastTSC) {
132 			clk=GetView().GetInputClockMS();
133 			if (tsc==LastTSC+1) {
134 				dt=(clk-LastClk)*0.001;
135 				if (dt>0.33) dt=0.33;
136 			}
137 			else {
138 				dt=0.01;
139 			}
140 			LastTSC=tsc;
141 			LastClk=clk;
142 			if (dt>0.0) {
143 				busy=CycleAnimation(dt);
144 			}
145 			else {
146 				busy=true;
147 			}
148 		}
149 		else {
150 			busy=true;
151 		}
152 		if (!busy && DeactivateWhenIdle) {
153 			Deactivate();
154 		}
155 	}
156 	else {
157 		busy=false;
158 	}
159 
160 	return busy;
161 }
162 
163 
164 //==============================================================================
165 //=========================== emKineticViewAnimator ============================
166 //==============================================================================
167 
emKineticViewAnimator(emView & view)168 emKineticViewAnimator::emKineticViewAnimator(emView & view)
169 	: emViewAnimator(view)
170 {
171 	Velocity[0]=0.0;
172 	Velocity[1]=0.0;
173 	Velocity[2]=0.0;
174 	ZoomFixPointCentered=true;
175 	ZoomFixX=0.0;
176 	ZoomFixY=0.0;
177 	FrictionEnabled=false;
178 	Friction=1000.0;
179 	Busy=false;
180 }
181 
182 
~emKineticViewAnimator()183 emKineticViewAnimator::~emKineticViewAnimator()
184 {
185 }
186 
187 
Activate()188 void emKineticViewAnimator::Activate()
189 {
190 	emKineticViewAnimator * oldKVA;
191 	emViewAnimator * va;
192 	double fixX,fixY;
193 	bool fixCentered;
194 
195 	if (!IsActive()) {
196 		oldKVA=NULL;
197 		for (va=GetView().GetActiveAnimator(); va; va=va->GetActiveSlave()) {
198 			oldKVA=dynamic_cast<emKineticViewAnimator*>(va);
199 			if (oldKVA) break;
200 		}
201 		if (oldKVA) {
202 			fixCentered=ZoomFixPointCentered;
203 			fixX=ZoomFixX;
204 			fixY=ZoomFixY;
205 			Velocity[0]=oldKVA->Velocity[0];
206 			Velocity[1]=oldKVA->Velocity[1];
207 			Velocity[2]=oldKVA->Velocity[2];
208 			ZoomFixPointCentered=oldKVA->ZoomFixPointCentered;
209 			ZoomFixX=oldKVA->ZoomFixX;
210 			ZoomFixY=oldKVA->ZoomFixY;
211 			if (fixCentered) {
212 				CenterZoomFixPoint();
213 			}
214 			else {
215 				SetZoomFixPoint(fixX,fixY);
216 			}
217 		}
218 		else {
219 			Velocity[0]=0.0;
220 			Velocity[1]=0.0;
221 			Velocity[2]=0.0;
222 		}
223 		emViewAnimator::Activate();
224 		UpdateBusyState();
225 	}
226 }
227 
228 
Deactivate()229 void emKineticViewAnimator::Deactivate()
230 {
231 	emViewAnimator::Deactivate();
232 }
233 
234 
GetAbsVelocity() const235 double emKineticViewAnimator::GetAbsVelocity() const
236 {
237 	return sqrt(
238 		Velocity[0]*Velocity[0] +
239 		Velocity[1]*Velocity[1] +
240 		Velocity[2]*Velocity[2]
241 	);
242 }
243 
244 
SetVelocity(int dimension,double velocity)245 void emKineticViewAnimator::SetVelocity(int dimension, double velocity)
246 {
247 	Velocity[dimension]=velocity;
248 	UpdateBusyState();
249 }
250 
251 
CenterZoomFixPoint()252 void emKineticViewAnimator::CenterZoomFixPoint()
253 {
254 	double oldFixX,oldFixY,f,q,dt;
255 
256 	if (!ZoomFixPointCentered) {
257 		oldFixX=ZoomFixX;
258 		oldFixY=ZoomFixY;
259 		ZoomFixPointCentered=true;
260 		UpdateZoomFixPoint();
261 		f=GetView().GetZoomFactorLogarithmPerPixel();
262 		dt=0.01;
263 		q=(1.0-exp(-Velocity[2]*dt*f))/dt;
264 		Velocity[0]+=(oldFixX-ZoomFixX)*q;
265 		Velocity[1]+=(oldFixY-ZoomFixY)*q;
266 	}
267 }
268 
269 
SetZoomFixPoint(double zoomFixX,double zoomFixY)270 void emKineticViewAnimator::SetZoomFixPoint(double zoomFixX, double zoomFixY)
271 {
272 	double oldFixX,oldFixY,f,q,dt;
273 
274 	if (
275 		ZoomFixPointCentered ||
276 		ZoomFixX!=zoomFixX ||
277 		ZoomFixY!=zoomFixY
278 	) {
279 		UpdateZoomFixPoint();
280 		oldFixX=ZoomFixX;
281 		oldFixY=ZoomFixY;
282 		ZoomFixPointCentered=false;
283 		ZoomFixX=zoomFixX;
284 		ZoomFixY=zoomFixY;
285 		f=GetView().GetZoomFactorLogarithmPerPixel();
286 		dt=0.01;
287 		q=(1.0-exp(-Velocity[2]*dt*f))/dt;
288 		Velocity[0]+=(oldFixX-ZoomFixX)*q;
289 		Velocity[1]+=(oldFixY-ZoomFixY)*q;
290 	}
291 }
292 
293 
SetFrictionEnabled(bool enabled)294 void emKineticViewAnimator::SetFrictionEnabled(bool enabled)
295 {
296 	FrictionEnabled=enabled;
297 }
298 
299 
SetFriction(double friction)300 void emKineticViewAnimator::SetFriction(double friction)
301 {
302 	Friction=friction;
303 }
304 
305 
CycleAnimation(double dt)306 bool emKineticViewAnimator::CycleAnimation(double dt)
307 {
308 	double v,v1,v2,f,a;
309 	double dist[3],done[3];
310 	int i;
311 
312 	if (Busy) {
313 
314 		if (IsFrictionEnabled()) {
315 			v=GetAbsVelocity();
316 			a=GetFriction();
317 			if (v-a*dt>0.0) {
318 				f=(v-a*dt)/v;
319 			}
320 			else if (v+a*dt<0.0) {
321 				f=(v+a*dt)/v;
322 			}
323 			else {
324 				f=0.0;
325 			}
326 		}
327 		else {
328 			f=1.0;
329 		}
330 
331 		for (i=0; i<3; i++) {
332 			v1=Velocity[i];
333 			v2=v1*f;
334 			Velocity[i]=v2;
335 			dist[i]=(v1+v2)*0.5*dt;
336 			done[i]=0.0;
337 		}
338 
339 		if (fabs(dist[0])>=0.01 || fabs(dist[1])>=0.01 || fabs(dist[2])>=0.01) {
340 			UpdateZoomFixPoint();
341 			GetView().RawScrollAndZoom(
342 				ZoomFixX,ZoomFixY,
343 				dist[0],dist[1],dist[2],
344 				NULL,
345 				&done[0],&done[1],&done[2]
346 			);
347 			GetView().SetActivePanelBestPossible();
348 		}
349 
350 		for (i=0; i<3; i++) {
351 			if (fabs(done[i])<0.99*fabs(dist[i])) {
352 				Velocity[i]=0.0;
353 			}
354 		}
355 
356 		UpdateBusyState();
357 	}
358 
359 	return Busy;
360 }
361 
362 
UpdateBusyState()363 void emKineticViewAnimator::UpdateBusyState()
364 {
365 	if (IsActive() && GetAbsVelocity()>0.01) {
366 		if (!Busy) {
367 			Busy=true;
368 			WakeUp();
369 		}
370 	}
371 	else {
372 		Velocity[0]=0.0;
373 		Velocity[1]=0.0;
374 		Velocity[2]=0.0;
375 		Busy=false;
376 	}
377 }
378 
379 
UpdateZoomFixPoint()380 void emKineticViewAnimator::UpdateZoomFixPoint()
381 {
382 	double sx,sy,sw,sh,x1,y1,x2,y2;
383 
384 	if (ZoomFixPointCentered) {
385 		x1=GetView().GetCurrentX();
386 		y1=GetView().GetCurrentY();
387 		x2=x1+GetView().GetCurrentWidth();
388 		y2=y1+GetView().GetCurrentHeight();
389 		if (GetView().IsPoppedUp()) {
390 			GetView().GetMaxPopupViewRect(&sx,&sy,&sw,&sh);
391 			if (x1<sx) x1=sx;
392 			if (y1<sy) y1=sy;
393 			if (x2>sx+sw) x2=sx+sw;
394 			if (y2>sy+sh) y2=sy+sh;
395 		}
396 		ZoomFixX=(x1+x2)*0.5;
397 		ZoomFixY=(y1+y2)*0.5;
398 	}
399 }
400 
401 
402 //==============================================================================
403 //=========================== emSpeedingViewAnimator ===========================
404 //==============================================================================
405 
emSpeedingViewAnimator(emView & view)406 emSpeedingViewAnimator::emSpeedingViewAnimator(emView & view)
407 	: emKineticViewAnimator(view)
408 {
409 	TargetVelocity[0]=0.0;
410 	TargetVelocity[1]=0.0;
411 	TargetVelocity[2]=0.0;
412 	Acceleration=1.0;
413 	ReverseAcceleration=1.0;
414 	Busy=false;
415 }
416 
417 
~emSpeedingViewAnimator()418 emSpeedingViewAnimator::~emSpeedingViewAnimator()
419 {
420 }
421 
422 
Activate()423 void emSpeedingViewAnimator::Activate()
424 {
425 	if (!IsActive()) {
426 		emKineticViewAnimator::Activate();
427 		UpdateBusyState();
428 	}
429 }
430 
431 
Deactivate()432 void emSpeedingViewAnimator::Deactivate()
433 {
434 	emKineticViewAnimator::Deactivate();
435 }
436 
437 
GetAbsTargetVelocity() const438 double emSpeedingViewAnimator::GetAbsTargetVelocity() const
439 {
440 	return sqrt(
441 		TargetVelocity[0]*TargetVelocity[0] +
442 		TargetVelocity[1]*TargetVelocity[1] +
443 		TargetVelocity[2]*TargetVelocity[2]
444 	);
445 }
446 
447 
SetTargetVelocity(int dimension,double targetVelocity)448 void emSpeedingViewAnimator::SetTargetVelocity(int dimension, double targetVelocity)
449 {
450 	TargetVelocity[dimension]=targetVelocity;
451 	UpdateBusyState();
452 }
453 
454 
SetAcceleration(double acceleration)455 void emSpeedingViewAnimator::SetAcceleration(double acceleration)
456 {
457 	Acceleration=acceleration;
458 }
459 
460 
SetReverseAcceleration(double reverseAcceleration)461 void emSpeedingViewAnimator::SetReverseAcceleration(double reverseAcceleration)
462 {
463 	ReverseAcceleration=reverseAcceleration;
464 }
465 
466 
CycleAnimation(double dt)467 bool emSpeedingViewAnimator::CycleAnimation(double dt)
468 {
469 	double v1,v2,vt,adt;
470 	bool frictionEnabled,baseBusy;
471 	int i;
472 
473 	if (Busy) {
474 		frictionEnabled=IsFrictionEnabled();
475 		for (i=0; i<3; i++) {
476 			v1=GetVelocity(i);
477 			vt=TargetVelocity[i];
478 			if (v1*vt<-0.1) adt=ReverseAcceleration*dt;
479 			else if (fabs(v1)<fabs(vt)) adt=Acceleration*emMin(dt,0.1);
480 			else if (frictionEnabled) adt=GetFriction()*dt;
481 			else adt=0.0;
482 			if (v1-adt>vt) {
483 				v2=v1-adt;
484 			}
485 			else if (v1+adt<vt) {
486 				v2=v1+adt;
487 			}
488 			else {
489 				v2=vt;
490 			}
491 			SetVelocity(i,v2);
492 		}
493 		SetFrictionEnabled(false);
494 		baseBusy=emKineticViewAnimator::CycleAnimation(dt);
495 		SetFrictionEnabled(frictionEnabled);
496 		UpdateBusyState();
497 	}
498 	else {
499 		baseBusy=emKineticViewAnimator::CycleAnimation(dt);
500 	}
501 	return Busy || baseBusy;
502 }
503 
504 
UpdateBusyState()505 void emSpeedingViewAnimator::UpdateBusyState()
506 {
507 	if (IsActive() && GetAbsTargetVelocity()>0.01) {
508 		if (!Busy) {
509 			Busy=true;
510 			WakeUp();
511 		}
512 	}
513 	else {
514 		Busy=false;
515 	}
516 }
517 
518 
519 //==============================================================================
520 //=========================== emSwipingViewAnimator ============================
521 //==============================================================================
522 
emSwipingViewAnimator(emView & view)523 emSwipingViewAnimator::emSwipingViewAnimator(emView & view)
524 	: emKineticViewAnimator(view)
525 {
526 	Gripped=false;
527 	SpringExtension[0]=0.0;
528 	SpringExtension[1]=0.0;
529 	SpringExtension[2]=0.0;
530 	InstantaneousVelocity[0]=GetVelocity(0);
531 	InstantaneousVelocity[1]=GetVelocity(1);
532 	InstantaneousVelocity[2]=GetVelocity(2);
533 	SpringConstant=1.0;
534 	Busy=false;
535 }
536 
537 
~emSwipingViewAnimator()538 emSwipingViewAnimator::~emSwipingViewAnimator()
539 {
540 }
541 
542 
Activate()543 void emSwipingViewAnimator::Activate()
544 {
545 	if (!IsActive()) {
546 		emKineticViewAnimator::Activate();
547 		SpringExtension[0]=0.0;
548 		SpringExtension[1]=0.0;
549 		SpringExtension[2]=0.0;
550 		InstantaneousVelocity[0]=GetVelocity(0);
551 		InstantaneousVelocity[1]=GetVelocity(1);
552 		InstantaneousVelocity[2]=GetVelocity(2);
553 		UpdateBusyState();
554 	}
555 }
556 
557 
Deactivate()558 void emSwipingViewAnimator::Deactivate()
559 {
560 	if (IsActive()) {
561 		SpringExtension[0]=0.0;
562 		SpringExtension[1]=0.0;
563 		SpringExtension[2]=0.0;
564 		emKineticViewAnimator::Deactivate();
565 	}
566 }
567 
568 
SetGripped(bool gripped)569 void emSwipingViewAnimator::SetGripped(bool gripped)
570 {
571 	if (Gripped!=gripped) {
572 		Gripped=gripped;
573 		if (!Gripped) {
574 			SpringExtension[0]=0.0;
575 			SpringExtension[1]=0.0;
576 			SpringExtension[2]=0.0;
577 			InstantaneousVelocity[0]=GetVelocity(0);
578 			InstantaneousVelocity[1]=GetVelocity(1);
579 			InstantaneousVelocity[2]=GetVelocity(2);
580 		}
581 	}
582 }
583 
584 
MoveGrip(int dimension,double distance)585 void emSwipingViewAnimator::MoveGrip(int dimension, double distance)
586 {
587 	if (Gripped) {
588 		SpringExtension[dimension]+=distance;
589 		UpdateBusyState();
590 	}
591 }
592 
593 
SetSpringConstant(double springConstant)594 void emSwipingViewAnimator::SetSpringConstant(double springConstant)
595 {
596 	SpringConstant=springConstant;
597 }
598 
599 
GetAbsSpringExtension() const600 double emSwipingViewAnimator::GetAbsSpringExtension() const
601 {
602 	return sqrt(
603 		SpringExtension[0]*SpringExtension[0] +
604 		SpringExtension[1]*SpringExtension[1] +
605 		SpringExtension[2]*SpringExtension[2]
606 	);
607 }
608 
609 
CycleAnimation(double dt)610 bool emSwipingViewAnimator::CycleAnimation(double dt)
611 {
612 	double v1,v2,e1,e2,w;
613 	bool frictionEnabled,baseBusy;
614 	int i;
615 
616 	if (Busy && Gripped) {
617 		for (i=0; i<3; i++) {
618 			e1=SpringExtension[i];
619 			v1=InstantaneousVelocity[i];
620 			if (SpringConstant<1E5 && fabs(SpringExtension[i]/dt)>20.0) {
621 				// Critically damped spring.
622 				w=sqrt(SpringConstant);
623 				e2=(e1+(e1*w-v1)*dt)*exp(-w*dt);
624 				v2=(v1+(e1*w-v1)*dt*w)*exp(-w*dt);
625 			}
626 			else {
627 				v2=0.0;
628 				e2=0.0;
629 			}
630 			SpringExtension[i]=e2;
631 			InstantaneousVelocity[i]=v2;
632 			SetVelocity(i,(e1-e2)/dt);
633 		}
634 		frictionEnabled=IsFrictionEnabled();
635 		SetFrictionEnabled(false);
636 		baseBusy=emKineticViewAnimator::CycleAnimation(dt);
637 		SetFrictionEnabled(frictionEnabled);
638 	}
639 	else {
640 		baseBusy=emKineticViewAnimator::CycleAnimation(dt);
641 	}
642 	UpdateBusyState();
643 	return Busy || baseBusy;
644 }
645 
646 
UpdateBusyState()647 void emSwipingViewAnimator::UpdateBusyState()
648 {
649 	if (
650 		IsActive() && Gripped &&
651 		(GetAbsSpringExtension()>0.01 || GetAbsVelocity()>0.01)
652 	) {
653 		if (!Busy) {
654 			Busy=true;
655 			WakeUp();
656 		}
657 	}
658 	else {
659 		SpringExtension[0]=0.0;
660 		SpringExtension[1]=0.0;
661 		SpringExtension[2]=0.0;
662 		Busy=false;
663 	}
664 }
665 
666 
667 //==============================================================================
668 //=========================== emMagneticViewAnimator ===========================
669 //==============================================================================
670 
emMagneticViewAnimator(emView & view)671 emMagneticViewAnimator::emMagneticViewAnimator(emView & view)
672 	: emKineticViewAnimator(view)
673 {
674 	CoreConfig=emCoreConfig::Acquire(view.GetRootContext());
675 	MagnetismActive=false;
676 	SetDeactivateWhenIdle();
677 }
678 
679 
~emMagneticViewAnimator()680 emMagneticViewAnimator::~emMagneticViewAnimator()
681 {
682 }
683 
684 
Activate()685 void emMagneticViewAnimator::Activate()
686 {
687 	emKineticViewAnimator * oldKVA;
688 	emViewAnimator * va;
689 
690 	if (!IsActive()) {
691 		MagnetismActive=false;
692 		oldKVA=NULL;
693 		for (va=GetView().GetActiveAnimator(); va; va=va->GetActiveSlave()) {
694 			oldKVA=dynamic_cast<emKineticViewAnimator*>(va);
695 			if (oldKVA) break;
696 		}
697 		if (oldKVA) {
698 			SetFriction(oldKVA->GetFriction());
699 			SetFrictionEnabled(oldKVA->IsFrictionEnabled());
700 		}
701 		else {
702 			SetFriction(1E10);
703 			SetFrictionEnabled(true);
704 		}
705 		emKineticViewAnimator::Activate();
706 	}
707 }
708 
709 
Deactivate()710 void emMagneticViewAnimator::Deactivate()
711 {
712 	emKineticViewAnimator::Deactivate();
713 }
714 
715 
CycleAnimation(double dt)716 bool emMagneticViewAnimator::CycleAnimation(double dt)
717 {
718 	double radiusFactor,minRadiusFactor,speedFactor,maxSpeedFactor;
719 	double x,y,w,h,v,d,t,fdt,k,a,absDist,maxDist;
720 	double dist[3];
721 	bool busy,frictionEnabled;
722 
723 	radiusFactor=CoreConfig->MagnetismRadius;
724 	minRadiusFactor=CoreConfig->MagnetismRadius.GetMinValue();
725 	speedFactor=CoreConfig->MagnetismSpeed;
726 	maxSpeedFactor=CoreConfig->MagnetismSpeed.GetMaxValue();
727 
728 	GetViewRect(&x,&y,&w,&h);
729 	maxDist=(w+h)*0.09*radiusFactor;
730 	if (radiusFactor<=minRadiusFactor*1.0001) {
731 		maxDist=0.0;
732 	}
733 
734 	absDist=CalculateDistance(&dist[0],&dist[1],&dist[2]);
735 
736 	busy=false;
737 
738 	if (absDist<=maxDist && absDist>1E-3) {
739 		if (!MagnetismActive && GetAbsVelocity()<10.0) {
740 			CenterZoomFixPoint();
741 			MagnetismActive=true;
742 		}
743 		busy=true;
744 	}
745 	else {
746 		if (MagnetismActive) {
747 			SetVelocity(0,0.0);
748 			SetVelocity(1,0.0);
749 			SetVelocity(2,0.0);
750 			MagnetismActive=false;
751 		}
752 		if (GetAbsVelocity()>=0.01) {
753 			busy=true;
754 		}
755 	}
756 
757 	if (MagnetismActive) {
758 		if (speedFactor>=maxSpeedFactor*0.9999 || absDist<1.0) {
759 			v=absDist/dt;
760 		}
761 		else {
762 			v=(
763 				GetVelocity(0)*dist[0] +
764 				GetVelocity(1)*dist[1] +
765 				GetVelocity(2)*dist[2]
766 			)/absDist;
767 			if (v<0.0) v=0.0;
768 
769 			d=0.0;
770 			t=0.0;
771 			for (;;) {
772 				fdt = emMin(dt-t,0.01);
773 				if (fdt<1E-10) break;
774 
775 				// Slope of hill.
776 				k=(absDist-d)/maxDist*4.0;
777 				if (fabs(k)>1.0) k=1.0/k;
778 
779 				// Acceleration through rolling downhill.
780 				a=k*maxDist*25.0*speedFactor*speedFactor;
781 
782 				// Damping
783 				a-=fabs(v)*15.0*speedFactor;
784 
785 				v+=a*fdt;
786 				d+=v*fdt;
787 				if (d>=absDist) {
788 					d=absDist;
789 					break;
790 				}
791 				t+=fdt;
792 			}
793 			v=d/dt;
794 		}
795 		SetVelocity(0,v*dist[0]/absDist);
796 		SetVelocity(1,v*dist[1]/absDist);
797 		SetVelocity(2,v*dist[2]/absDist);
798 	}
799 
800 	frictionEnabled=IsFrictionEnabled();
801 	SetFrictionEnabled(frictionEnabled && !MagnetismActive);
802 	if (emKineticViewAnimator::CycleAnimation(dt)) busy=true;
803 	SetFrictionEnabled(frictionEnabled);
804 
805 	return busy;
806 }
807 
808 
CalculateDistance(double * pDX,double * pDY,double * pDZ) const809 double emMagneticViewAnimator::CalculateDistance(
810 	double * pDX, double * pDY, double * pDZ
811 ) const
812 {
813 	double dd,vx,vy,vw,vh,zflpp,x,y,w,h,tx,ty,tz,td;
814 	emPanel * svp, * p;
815 
816 	*pDX=1E+10;
817 	*pDY=1E+10;
818 	*pDZ=1E+10;
819 	dd=3E+100;
820 
821 	if ((GetView().GetViewFlags()&emView::VF_POPUP_ZOOM)!=0) {
822 		// ??? emMagneticViewAnimator is still not functioning
823 		// ??? properly with pop-up-zoom.
824 		return sqrt(dd);
825 	}
826 
827 	svp=GetView().GetSupremeViewedPanel();
828 	if (svp) {
829 		GetViewRect(&vx,&vy,&vw,&vh);
830 		zflpp=GetView().GetZoomFactorLogarithmPerPixel();
831 		for (p=svp;;) {
832 			if (p->IsViewed() && p->IsFocusable()) {
833 				p->GetEssenceRect(&x,&y,&w,&h);
834 				x=p->PanelToViewX(x);
835 				y=p->PanelToViewY(y);
836 				w=p->PanelToViewDeltaX(w);
837 				h=p->PanelToViewDeltaY(h);
838 				if (w>1E-3 && h>1E-3) {
839 					// Maximize panel in view (centered).
840 					tx=(x+w*0.5)-(vx+vw*0.5);
841 					ty=(y+h*0.5)-(vy+vh*0.5);
842 					if (w*vh>=h*vw) {
843 						tz=log(vw/w)/zflpp;
844 					}
845 					else {
846 						tz=log(vh/h)/zflpp;
847 					}
848 					td=tx*tx+ty*ty+tz*tz;
849 					if (td<dd) {
850 						*pDX=tx;
851 						*pDY=ty;
852 						*pDZ=tz;
853 						dd=td;
854 					}
855 					// Maximize view in panel.
856 					/*??? Helpful only for certain panel types, disturbing for others...
857 					static const double minViewInPanelFac = 1.5;
858 					if (w*vh>=h*vw*minViewInPanelFac) {
859 						tx=x-vx;
860 						if (tx<0.0) {
861 							tx=(x+w)-(vx+vw);
862 							if (tx>0.0) tx=0.0;
863 						}
864 						ty=(y+h*0.5)-(vy+vh*0.5);
865 						tz=log(vh/h)/zflpp;
866 						td=tx*tx+ty*ty+tz*tz;
867 						if (td<dd) {
868 							*pDX=tx;
869 							*pDY=ty;
870 							*pDZ=tz;
871 							dd=td;
872 						}
873 					}
874 					else if (h*vw>=w*vh*minViewInPanelFac) {
875 						tx=(x+w*0.5)-(vx+vw*0.5);
876 						ty=y-vy;
877 						if (ty<0.0) {
878 							ty=(y+h)-(vy+vh);
879 							if (ty>0.0) ty=0.0;
880 						}
881 						tz=log(vw/w)/zflpp;
882 						td=tx*tx+ty*ty+tz*tz;
883 						if (td<dd) {
884 							*pDX=tx;
885 							*pDY=ty;
886 							*pDZ=tz;
887 							dd=td;
888 						}
889 					}
890 					*/
891 				}
892 			}
893 			if (p->GetFirstChild()) p=p->GetFirstChild();
894 			else if (p==svp) break;
895 			else if (p->GetNext()) p=p->GetNext();
896 			else {
897 				do {
898 					p=p->GetParent();
899 				} while (p!=svp && !p->GetNext());
900 				if (p==svp) break;
901 				p=p->GetNext();
902 			}
903 		}
904 	}
905 
906 	return sqrt(dd);
907 }
908 
909 
GetViewRect(double * pX,double * pY,double * pW,double * pH) const910 void emMagneticViewAnimator::GetViewRect(
911 	double * pX, double * pY, double * pW, double * pH
912 ) const
913 {
914 	if ((GetView().GetViewFlags()&emView::VF_POPUP_ZOOM)!=0) {
915 		GetView().GetMaxPopupViewRect(pX,pY,pW,pH);
916 	}
917 	else {
918 		*pX=GetView().GetHomeX();
919 		*pY=GetView().GetHomeY();
920 		*pW=GetView().GetHomeWidth();
921 		*pH=GetView().GetHomeHeight();
922 	}
923 }
924 
925 
926 //==============================================================================
927 //=========================== emVisitingViewAnimator ===========================
928 //==============================================================================
929 
emVisitingViewAnimator(emView & view)930 emVisitingViewAnimator::emVisitingViewAnimator(emView & view)
931 	: emViewAnimator(view)
932 {
933 	Animated=false;
934 	Acceleration=5.0;
935 	MaxCuspSpeed=2.0;
936 	MaxAbsoluteSpeed=5.0;
937 	State=ST_NO_GOAL;
938 	VisitType=VT_VISIT;
939 	RelX=RelY=RelA=0;
940 	Adherent=false;
941 	UtilizeView=false;
942 	MaxDepthSeen=-1;
943 	Speed=0.0;
944 	TimeSlicesWithoutHope=0;
945 	GiveUpClock=0;
946 	SetDeactivateWhenIdle();
947 }
948 
949 
~emVisitingViewAnimator()950 emVisitingViewAnimator::~emVisitingViewAnimator()
951 {
952 }
953 
954 
SetAnimated(bool animated)955 void emVisitingViewAnimator::SetAnimated(bool animated)
956 {
957 	Animated=animated;
958 }
959 
960 
SetAcceleration(double acceleration)961 void emVisitingViewAnimator::SetAcceleration(double acceleration)
962 {
963 	Acceleration=acceleration;
964 }
965 
966 
SetMaxCuspSpeed(double maxCuspSpeed)967 void emVisitingViewAnimator::SetMaxCuspSpeed(double maxCuspSpeed)
968 {
969 	MaxCuspSpeed=maxCuspSpeed;
970 }
971 
972 
SetMaxAbsoluteSpeed(double maxAbsoluteSpeed)973 void emVisitingViewAnimator::SetMaxAbsoluteSpeed(double maxAbsoluteSpeed)
974 {
975 	MaxAbsoluteSpeed=maxAbsoluteSpeed;
976 }
977 
978 
SetAnimParamsByCoreConfig(const emCoreConfig & coreConfig)979 void emVisitingViewAnimator::SetAnimParamsByCoreConfig(const emCoreConfig & coreConfig)
980 {
981 	double f,fMax;
982 
983 	f=coreConfig.VisitSpeed;
984 	fMax=coreConfig.VisitSpeed.GetMaxValue();
985 
986 	Animated=(f<fMax*0.99999);
987 	Acceleration=35.0*f;
988 	MaxAbsoluteSpeed=35.0*f;
989 	MaxCuspSpeed=MaxAbsoluteSpeed*0.5;
990 }
991 
992 
SetGoal(const char * identity,bool adherent,const char * subject)993 void emVisitingViewAnimator::SetGoal(
994 	const char * identity, bool adherent, const char * subject
995 )
996 {
997 	SetGoal(VT_VISIT,identity,0.0,0.0,0.0,adherent,false,subject);
998 }
999 
1000 
SetGoal(const char * identity,double relX,double relY,double relA,bool adherent,const char * subject)1001 void emVisitingViewAnimator::SetGoal(
1002 	const char * identity, double relX, double relY, double relA,
1003 	bool adherent, const char * subject
1004 )
1005 {
1006 	SetGoal(VT_VISIT_REL,identity,relX,relY,relA,adherent,false,subject);
1007 }
1008 
1009 
SetGoalFullsized(const char * identity,bool adherent,bool utilizeView,const char * subject)1010 void emVisitingViewAnimator::SetGoalFullsized(
1011 	const char * identity, bool adherent, bool utilizeView, const char * subject
1012 )
1013 {
1014 	SetGoal(VT_VISIT_FULLSIZED,identity,0.0,0.0,0.0,adherent,utilizeView,subject);
1015 }
1016 
1017 
ClearGoal()1018 void emVisitingViewAnimator::ClearGoal()
1019 {
1020 	if (State!=ST_NO_GOAL) {
1021 		State=ST_NO_GOAL;
1022 		VisitType=VT_VISIT;
1023 		Identity.Clear();
1024 		RelX=RelY=RelA=0;
1025 		Adherent=false;
1026 		UtilizeView=false;
1027 		Subject.Clear();
1028 		Names.Clear();
1029 		if (IsActive()) {
1030 			GetView().SetSeekPos(NULL,NULL);
1031 			MaxDepthSeen=-1;
1032 			TimeSlicesWithoutHope=0;
1033 			GiveUpClock=0;
1034 			InvalidatePainting();
1035 		}
1036 	}
1037 }
1038 
1039 
Activate()1040 void emVisitingViewAnimator::Activate()
1041 {
1042 	if (!IsActive()) {
1043 		emViewAnimator::Activate();
1044 		if (State!=ST_NO_GOAL) {
1045 			State=ST_CURVE;
1046 			MaxDepthSeen=-1;
1047 			TimeSlicesWithoutHope=0;
1048 			GiveUpClock=0;
1049 			Speed=0.0;
1050 			WakeUp();
1051 		}
1052 	}
1053 }
1054 
1055 
Deactivate()1056 void emVisitingViewAnimator::Deactivate()
1057 {
1058 	if (IsActive()) {
1059 		emViewAnimator::Deactivate();
1060 		GetView().SetSeekPos(NULL,NULL);
1061 		InvalidatePainting();
1062 	}
1063 }
1064 
1065 
Input(emInputEvent & event,const emInputState & state)1066 void emVisitingViewAnimator::Input(emInputEvent & event, const emInputState & state)
1067 {
1068 	if (!IsActive() || (State!=ST_SEEK && State!=ST_GIVING_UP)) return;
1069 
1070 	if (!event.IsEmpty()) {
1071 		event.Eat();
1072 		Deactivate();
1073 	}
1074 }
1075 
1076 
Paint(const emPainter & painter) const1077 void emVisitingViewAnimator::Paint(const emPainter & painter) const
1078 {
1079 	double vx,vy,vw,vh,f,x,y,w,h,ws,tw,ch;
1080 	emString str;
1081 	int l1,l2;
1082 
1083 	if (!IsActive() || (State!=ST_SEEK && State!=ST_GIVING_UP)) return;
1084 
1085 	vx=GetView().GetCurrentX();
1086 	vy=GetView().GetCurrentY();
1087 	vw=GetView().GetCurrentWidth();
1088 	vh=GetView().GetCurrentHeight();
1089 
1090 	w=emMin(emMax(vw,vh)*0.6,vw);
1091 	h=w*0.25;
1092 	f=vh*0.8/h;
1093 	if (f<1.0) { w*=f; h*=f; }
1094 	x=vx+(vw-w)*0.5;
1095 	y=emMax(vy+vh*0.5-h*1.25,vy);
1096 
1097 	painter.PaintRoundRect(
1098 		x+w*0.03,y+w*0.03,
1099 		w,h,
1100 		h*0.2,h*0.2,
1101 		emColor(0,0,0,160)
1102 	);
1103 	painter.PaintRoundRect(
1104 		x,y,
1105 		w,h,
1106 		h*0.2,h*0.2,
1107 		emColor(34,102,153,208)
1108 	);
1109 	painter.PaintRoundRectOutline(
1110 		x+h*0.03,y+h*0.03,
1111 		w-h*0.06,h-h*0.06,
1112 		h*0.2-h*0.06*0.5,h*0.2-h*0.06*0.5,
1113 		h*0.02,
1114 		emColor(221,221,221)
1115 	);
1116 
1117 	x+=h*0.2;
1118 	y+=h*0.1;
1119 	w-=h*0.4;
1120 	h-=h*0.2;
1121 
1122 	if (State == ST_GIVING_UP) {
1123 		painter.PaintTextBoxed(
1124 			x,y,w,h,
1125 			"Not found",
1126 			h*0.6,
1127 			emColor(255,136,136),
1128 			0,
1129 			EM_ALIGN_CENTER,
1130 			EM_ALIGN_LEFT,
1131 			0.8
1132 		);
1133 		return;
1134 	}
1135 
1136 	str="Seeking";
1137 	if (!Subject.IsEmpty()) {
1138 		str+=" for ";
1139 		str+=Subject;
1140 	}
1141 	painter.PaintTextBoxed(
1142 		x,y,w,h*0.4,
1143 		str,
1144 		h,
1145 		emColor(221,221,221),
1146 		0,
1147 		EM_ALIGN_CENTER,
1148 		EM_ALIGN_LEFT,
1149 		0.8
1150 	);
1151 
1152 	painter.PaintTextBoxed(
1153 		x,y+h*0.8,w,h*0.2,
1154 		"Press any keyboard key or mouse button to abort.",
1155 		h,
1156 		emColor(221,221,221),
1157 		0,
1158 		EM_ALIGN_CENTER,
1159 		EM_ALIGN_LEFT,
1160 		0.8
1161 	);
1162 
1163 	y+=h*0.5;
1164 	h*=0.2;
1165 	if (GetView().SeekPosPanel) str=GetView().SeekPosPanel->GetIdentity();
1166 	else str="";
1167 	l1=strlen(str);
1168 	l2=strlen(Identity);
1169 	if (l1>l2) l1=l2;
1170 	tw=painter.GetTextSize(Identity,h,false);
1171 	ws=1.0;
1172 	if (tw>w) { ws=w/tw; tw=w; }
1173 	ch=h;
1174 	if (ws<0.5) { ch*=ws/0.5; ws=0.5; }
1175 	painter.PaintRect(
1176 		x+(w-tw)*0.5,y,tw*l1/l2,h,
1177 		emColor(136,255,136,80)
1178 	);
1179 	painter.PaintRect(
1180 		x+(w-tw)*0.5+tw*l1/l2,y,tw*(l2-l1)/l2,h,
1181 		emColor(136,136,136,80)
1182 	);
1183 	painter.PaintText(
1184 		x+(w-tw)*0.5,y+(h-ch)*0.5,
1185 		Identity,ch,ws,emColor(136,255,136),0,l1
1186 	);
1187 	painter.PaintText(
1188 		x+(w-tw)*0.5+tw*l1/l2,y+(h-ch)*0.5,
1189 		Identity.Get()+l1,ch,ws,emColor(136,136,136),0,l2-l1
1190 	);
1191 }
1192 
1193 
CycleAnimation(double dt)1194 bool emVisitingViewAnimator::CycleAnimation(double dt)
1195 {
1196 	double relX,relY,relA,distFinal,dirX,dirY,distXY,distZ;
1197 	double curveDist,curvePos,deltaX,deltaY,deltaZ,deltaXY,delta;
1198 	double zflpp,vx,vy,vw,vh,doneX,doneY,doneZ,done;
1199 	int depth,panelsAfter;
1200 	bool adherent;
1201 	emPanel * nep, * panel;
1202 
1203 	switch (State) {
1204 	case ST_NO_GOAL:
1205 	case ST_GIVEN_UP:
1206 	case ST_GOAL_REACHED:
1207 		return false;
1208 	case ST_GIVING_UP:
1209 		if (emGetClockMS()<GiveUpClock+1500) {
1210 			return true;
1211 		}
1212 		State=ST_GIVEN_UP;
1213 		return false;
1214 	case ST_CURVE:
1215 	case ST_DIRECT:
1216 	case ST_SEEK:
1217 		break;
1218 	}
1219 
1220 	nep=GetNearestExistingPanel(
1221 		&relX,&relY,&relA,&adherent,&depth,&panelsAfter,&distFinal
1222 	);
1223 	if (!nep) {
1224 		State=ST_GIVING_UP;
1225 		GiveUpClock=emGetClockMS();
1226 		InvalidatePainting();
1227 		return true;
1228 	}
1229 
1230 	// Activate nearest existing panel. But remember, that while the
1231 	// animation is running, the goal panel may forward the activation
1232 	// to a child while making itself unfocusable.
1233 	if (nep->IsFocusable()) {
1234 		nep->Activate(adherent);
1235 	}
1236 	else {
1237 		panel=nep;
1238 		while (panel->GetParent() && !panel->IsFocusable()) {
1239 			panel=panel->GetParent();
1240 		}
1241 		if (!panel->IsInActivePath()) {
1242 			panel->Activate(adherent);
1243 		}
1244 	}
1245 
1246 	if (Animated) {
1247 		if (MaxDepthSeen<depth) {
1248 			if (State==ST_SEEK) {
1249 				GetView().SetSeekPos(NULL,NULL);
1250 				State=ST_CURVE;
1251 			}
1252 			MaxDepthSeen=depth;
1253 		}
1254 	}
1255 	else {
1256 		State=ST_SEEK;
1257 		if (MaxDepthSeen<depth) {
1258 			MaxDepthSeen=depth;
1259 		}
1260 	}
1261 
1262 	if (State==ST_CURVE || State==ST_DIRECT) {
1263 
1264 		GetDistanceTo(nep,relX,relY,relA,&dirX,&dirY,&distXY,&distZ);
1265 
1266 		if (State==ST_DIRECT) {
1267 			curvePos=0.0;
1268 			curveDist=GetDirectDist(distXY,distZ);
1269 		}
1270 		else {
1271 			GetCurvePosDist(distXY,distZ,&curvePos,&curveDist);
1272 		}
1273 
1274 		UpdateSpeed(curvePos,curveDist,panelsAfter,distFinal,dt);
1275 
1276 		if (State==ST_DIRECT) {
1277 			GetDirectPoint(distXY,distZ,Speed*dt,&deltaXY,&deltaZ);
1278 		}
1279 		else {
1280 			CurvePoint cp1=GetCurvePoint(curvePos);
1281 			CurvePoint cp2=GetCurvePoint(curvePos+Speed*dt);
1282 			deltaXY=(cp2.X-cp1.X)*exp(cp1.Z);
1283 			deltaZ=(cp2.Z-cp1.Z);
1284 		}
1285 
1286 		zflpp=GetView().GetZoomFactorLogarithmPerPixel();
1287 		deltaXY/=zflpp;
1288 		deltaZ/=zflpp;
1289 		deltaX=dirX*deltaXY;
1290 		deltaY=dirY*deltaXY;
1291 
1292 		GetViewRect(&vx,&vy,&vw,&vh);
1293 		GetView().RawScrollAndZoom(
1294 			vx+vw*0.5, vy+vh*0.5,
1295 			deltaX, deltaY, deltaZ,
1296 			GetNearestViewedPanel(nep),
1297 			&doneX, &doneY, &doneZ
1298 		);
1299 
1300 		delta=sqrt(deltaX*deltaX+deltaY*deltaY+deltaZ*deltaZ);
1301 		done=sqrt(doneX*doneX+doneY*doneY+doneZ*doneZ);
1302 
1303 		if (curveDist<=1E-6) {
1304 			if (panelsAfter>0) {
1305 				State=ST_SEEK;
1306 			}
1307 			else {
1308 				State=ST_GOAL_REACHED;
1309 				return false;
1310 			}
1311 		}
1312 		else if (done < delta*0.2) {
1313 			if (State==ST_CURVE) {
1314 				State=ST_DIRECT;
1315 			}
1316 			else {
1317 				State=ST_SEEK;
1318 			}
1319 		}
1320 	}
1321 
1322 	if (State==ST_SEEK) {
1323 		if (depth+1>=Names.GetCount()) {
1324 			GetView().RawVisit(nep,relX,relY,relA);
1325 			State=ST_GOAL_REACHED;
1326 			return false;
1327 		}
1328 		else if (GetView().SeekPosPanel!=nep) {
1329 			GetView().SetSeekPos(nep,Names[depth+1]);
1330 			GetView().RawVisitFullsized(nep);
1331 			InvalidatePainting();
1332 			TimeSlicesWithoutHope=4;
1333 		}
1334 		else if (GetView().IsHopeForSeeking()) {
1335 			TimeSlicesWithoutHope=0;
1336 		}
1337 		else {
1338 			TimeSlicesWithoutHope++;
1339 			if (TimeSlicesWithoutHope>10) {
1340 				State=ST_GIVING_UP;
1341 				GiveUpClock=emGetClockMS();
1342 				InvalidatePainting();
1343 			}
1344 		}
1345 	}
1346 
1347 	return true;
1348 }
1349 
1350 
SetGoal(VisitTypeEnum visitType,const char * identity,double relX,double relY,double relA,bool adherent,bool utilizeView,const char * subject)1351 void emVisitingViewAnimator::SetGoal(
1352 	VisitTypeEnum visitType, const char * identity, double relX, double relY,
1353 	double relA, bool adherent, bool utilizeView, const char * subject
1354 )
1355 {
1356 	VisitType=visitType;
1357 	RelX=relX;
1358 	RelY=relY;
1359 	RelA=relA;
1360 	Adherent=adherent;
1361 	UtilizeView=utilizeView;
1362 	Subject=subject;
1363 	if (State==ST_NO_GOAL || Identity != identity) {
1364 		State=ST_CURVE;
1365 		Identity=identity;
1366 		Names=emPanel::DecodeIdentity(Identity);
1367 		if (IsActive()) {
1368 			GetView().SetSeekPos(NULL,NULL);
1369 			MaxDepthSeen=-1;
1370 			TimeSlicesWithoutHope=0;
1371 			GiveUpClock=0;
1372 			InvalidatePainting();
1373 		}
1374 	}
1375 }
1376 
1377 
UpdateSpeed(double pos,double dist,int panelsAfter,double distFinal,double dt)1378 void emVisitingViewAnimator::UpdateSpeed(
1379 	double pos, double dist, int panelsAfter, double distFinal, double dt
1380 )
1381 {
1382 	double s,v;
1383 
1384 	Speed+=Acceleration*dt;
1385 
1386 	s=dist+panelsAfter*log(2.0)+distFinal;
1387 	if (s<0.0) s=0.0;
1388 	v=sqrt(Acceleration*s*2.0);
1389 	if (Speed>v) Speed=v;
1390 
1391 	if (pos<0.0) {
1392 		v=sqrt(Acceleration*(-pos)*2.0+MaxCuspSpeed*MaxCuspSpeed);
1393 		if (Speed>v) Speed=v;
1394 	}
1395 
1396 	if (Speed>MaxAbsoluteSpeed) Speed=MaxAbsoluteSpeed;
1397 
1398 	if (Speed>dist/dt) Speed=dist/dt;
1399 }
1400 
1401 
GetNearestExistingPanel(double * pRelX,double * pRelY,double * pRelA,bool * pAdherent,int * pDepth,int * pPanelsAfter,double * pDistFinal) const1402 emPanel * emVisitingViewAnimator::GetNearestExistingPanel(
1403 	double * pRelX, double * pRelY, double * pRelA, bool * pAdherent,
1404 	int * pDepth, int * pPanelsAfter, double * pDistFinal
1405 ) const
1406 {
1407 	emPanel * p, * c;
1408 	int i;
1409 
1410 	p=GetView().GetRootPanel();
1411 	if (!p || Names.GetCount()<1 || Names[0]!=p->GetName()) {
1412 		*pRelX=0.0;
1413 		*pRelY=0.0;
1414 		*pRelA=0.0;
1415 		*pAdherent=false;
1416 		*pDepth=0;
1417 		*pPanelsAfter=Names.GetCount();
1418 		*pDistFinal=0.0;
1419 		return NULL;
1420 	}
1421 
1422 	for (i=1; i<Names.GetCount(); i++) {
1423 		c=p->GetChild(Names[i]);
1424 		if (!c) break;
1425 		p=c;
1426 	}
1427 
1428 	if (i<Names.GetCount()) {
1429 		GetView().CalcVisitFullsizedCoords(p,pRelX,pRelY,pRelA);
1430 		*pAdherent=false;
1431 		*pDepth=i-1;
1432 		*pPanelsAfter=Names.GetCount()-i;
1433 		switch (VisitType) {
1434 		case VT_VISIT:
1435 			*pDistFinal=0.0;
1436 			break;
1437 		case VT_VISIT_REL:
1438 			if (RelA<=0.0 || RelA>=1.0) {
1439 				*pDistFinal=0.0;
1440 			}
1441 			else {
1442 				*pDistFinal=log(1.0/sqrt(RelA));
1443 			}
1444 			break;
1445 		default:
1446 			*pDistFinal=0.0;
1447 			break;
1448 		}
1449 		return p;
1450 	}
1451 
1452 	switch (VisitType) {
1453 	case VT_VISIT:
1454 		GetView().CalcVisitCoords(p,pRelX,pRelY,pRelA);
1455 		break;
1456 	case VT_VISIT_REL:
1457 		if (RelA<=0.0) {
1458 			GetView().CalcVisitFullsizedCoords(p,pRelX,pRelY,pRelA,RelA<-0.9);
1459 		}
1460 		else {
1461 			*pRelX=RelX;
1462 			*pRelY=RelY;
1463 			*pRelA=RelA;
1464 		}
1465 		break;
1466 	default:
1467 		GetView().CalcVisitFullsizedCoords(p,pRelX,pRelY,pRelA,UtilizeView);
1468 		break;
1469 	}
1470 	*pAdherent=Adherent;
1471 	*pDepth=Names.GetCount()-1;
1472 	*pPanelsAfter=0;
1473 	*pDistFinal=0.0;
1474 	return p;
1475 }
1476 
1477 
GetNearestViewedPanel(emPanel * nearestExistingPanel) const1478 emPanel * emVisitingViewAnimator::GetNearestViewedPanel(
1479 	emPanel * nearestExistingPanel
1480 ) const
1481 {
1482 	emPanel * p;
1483 
1484 	p=nearestExistingPanel;
1485 	while (p && !p->IsInViewedPath()) {
1486 		p=p->GetParent();
1487 	}
1488 	while (
1489 		p &&
1490 		p->GetParent() &&
1491 		p->GetParent()->IsViewed() && (
1492 			!p->IsViewed() ||
1493 			p->GetViewedWidth()<GetView().GetCurrentWidth()*1E-5 ||
1494 			p->GetViewedHeight()<GetView().GetCurrentHeight()*1E-5
1495 		)
1496 	) {
1497 		p=p->GetParent();
1498 	}
1499 	if (p && !p->IsViewed()) {
1500 		p=GetView().GetSupremeViewedPanel();
1501 	}
1502 	return p;
1503 }
1504 
1505 
GetDistanceTo(emPanel * panel,double relX,double relY,double relA,double * pDirX,double * pDirY,double * pDistXY,double * pDistZ) const1506 void emVisitingViewAnimator::GetDistanceTo(
1507 	emPanel * panel, double relX, double relY, double relA,
1508 	double * pDirX, double * pDirY, double * pDistXY, double * pDistZ
1509 ) const
1510 {
1511 	double hx,hy,hw,hh,hp,sx,sy,sw,sh;
1512 	double vx,vy,vw,vh,ax,ay,aw,ah,bx,by,bw,bh;
1513 	double extremeDist,dx,dy,dz,dxy,t,f;
1514 	emPanel * b, * a;
1515 
1516 	// Home coordinates of the view.
1517 	hx=GetView().GetHomeX();
1518 	hy=GetView().GetHomeY();
1519 	hw=GetView().GetHomeWidth();
1520 	hh=GetView().GetHomeHeight();
1521 	hp=GetView().GetHomePixelTallness();
1522 
1523 	// Maximum coordinates of the view.
1524 	GetViewRect(&sx,&sy,&sw,&sh);
1525 
1526 	// Calculate rectangle "b": Where shall the view be at the end,
1527 	// in the target panel, in panel coordinates.
1528 	vw=sqrt(hw*hh*hp/(relA*panel->GetHeight()));
1529 	vh=vw*panel->GetHeight()/hp;
1530 	vx=hx+hw*0.5-(relX+0.5)*vw;
1531 	vy=hy+hh*0.5-(relY+0.5)*vh;
1532 	bx=(sx-vx)/vw;
1533 	by=(sy-vy)/vw;
1534 	bw=sw/vw;
1535 	bh=sh/vw;
1536 
1537 	// Go up with "b" in the tree until InViewedPath, but
1538 	// at least until SVP.
1539 	for (b=panel;;) {
1540 		if (!b->GetParent()) break;
1541 		if (b->IsInViewedPath() && !b->GetParent()->IsViewed()) break;
1542 		bx=b->GetLayoutX()+bx*b->GetLayoutWidth();
1543 		by=b->GetLayoutY()+by*b->GetLayoutWidth();
1544 		bw=bw*b->GetLayoutWidth();
1545 		bh=bh*b->GetLayoutWidth();
1546 		b=b->GetParent();
1547 	}
1548 
1549 	// Get SVP and rectangle "a" therein:  Where is the view now.
1550 	a=GetView().GetSupremeViewedPanel();
1551 	ax=(sx-a->GetViewedX())/a->GetViewedWidth();
1552 	ay=(sy-a->GetViewedY())/a->GetViewedWidth();
1553 	aw=sw/a->GetViewedWidth();
1554 	ah=sh/a->GetViewedWidth();
1555 
1556 	// Go up with "a" until reaching "b", so that both rectangles
1557 	// are in the same panel.
1558 	while (a!=b) {
1559 		ax=a->GetLayoutX()+ax*a->GetLayoutWidth();
1560 		ay=a->GetLayoutY()+ay*a->GetLayoutWidth();
1561 		aw=aw*a->GetLayoutWidth();
1562 		ah=ah*a->GetLayoutWidth();
1563 		a=a->GetParent();
1564 	}
1565 
1566 	// Calculate 3D distance.
1567 	extremeDist=50.0;
1568 	dx=bx-ax+(bw-aw)*0.5;
1569 	dy=by-ay+(bh-ah)*0.5;
1570 	t=aw+ah;
1571 	if (t<1E-100) {
1572 		dx=0.0;
1573 		dy=0.0;
1574 		dz=-extremeDist;
1575 	}
1576 	else {
1577 		f=(sw+sh)*GetView().GetZoomFactorLogarithmPerPixel();
1578 		dx=dx/t*f;
1579 		dy=dy/t*f;
1580 		t=(bw+bh)/t;
1581 		if (t<exp(-extremeDist)) {
1582 			dz = extremeDist;
1583 		}
1584 		else if (t>exp(extremeDist)) {
1585 			dz = -extremeDist;
1586 		}
1587 		else {
1588 			dz = -log(t);
1589 		}
1590 	}
1591 
1592 	// Calculate 2D distance.
1593 	dxy=sqrt(dx*dx+dy*dy);
1594 	if (dxy<1E-100) {
1595 		dxy=0.0;
1596 		*pDirX = 1.0;
1597 		*pDirY = 0.0;
1598 	}
1599 	else {
1600 		*pDirX = dx/dxy;
1601 		*pDirY = dy/dxy;
1602 	}
1603 	if (dxy>exp(extremeDist)) {
1604 		*pDistXY=0.0;
1605 		*pDistZ=-extremeDist;
1606 	}
1607 	else {
1608 		*pDistXY=dxy;
1609 		*pDistZ=dz;
1610 	}
1611 }
1612 
1613 
GetViewRect(double * pX,double * pY,double * pW,double * pH) const1614 void emVisitingViewAnimator::GetViewRect(
1615 	double * pX, double * pY, double * pW, double * pH
1616 ) const
1617 {
1618 	if ((GetView().GetViewFlags()&emView::VF_POPUP_ZOOM)!=0) {
1619 		GetView().GetMaxPopupViewRect(pX,pY,pW,pH);
1620 	}
1621 	else {
1622 		*pX=GetView().GetHomeX();
1623 		*pY=GetView().GetHomeY();
1624 		*pW=GetView().GetHomeWidth();
1625 		*pH=GetView().GetHomeHeight();
1626 	}
1627 }
1628 
1629 
GetDirectDist(double x,double z)1630 double emVisitingViewAnimator::GetDirectDist(double x, double z)
1631 {
1632 	double fixX;
1633 
1634 	if (fabs(z)<0.1) {
1635 		return sqrt(x*x+z*z);
1636 	}
1637 	else {
1638 		fixX = x/(1-exp(-z));
1639 		return fabs(z) * sqrt(fixX*fixX+1);
1640 	}
1641 }
1642 
1643 
GetDirectPoint(double x,double z,double d,double * pX,double * pZ)1644 void emVisitingViewAnimator::GetDirectPoint(
1645 	double x, double z, double d,
1646 	double * pX, double * pZ
1647 )
1648 {
1649 	double fixX,dist,t;
1650 
1651 	if (fabs(z)<0.1) {
1652 		dist=sqrt(x*x+z*z);
1653 		if (dist<1E-100) {
1654 			t=0.0;
1655 		}
1656 		else {
1657 			t=d/dist;
1658 		}
1659 		*pX = x*t;
1660 		*pZ = z*t;
1661 	}
1662 	else {
1663 		fixX = x/(1-exp(-z));
1664 		dist = fabs(z) * sqrt(fixX*fixX+1);
1665 		t = d/dist;
1666 		*pX = fixX * (1-exp(-z*t));
1667 		*pZ = z*t;
1668 	}
1669 }
1670 
1671 
GetCurvePosDist(double x,double z,double * pCurvePos,double * pCurveDist)1672 void emVisitingViewAnimator::GetCurvePosDist(
1673 	double x, double z, double * pCurvePos, double * pCurveDist
1674 )
1675 {
1676 	double a,b,aMin,aMax,bMin,bMax;
1677 	CurvePoint ap,bp,tp;
1678 	bool neg,swap;
1679 	int i,j;
1680 
1681 	neg=false;
1682 	swap=false;
1683 	if (z<0) {
1684 		z=-z;
1685 		x/=exp(z);
1686 		neg=true;
1687 		swap=true;
1688 	}
1689 	if (x<0) {
1690 		x=-x;
1691 		neg=!neg;
1692 	}
1693 
1694 	aMin=-x;
1695 	aMax=CurveMaxIndex*CurveDeltaDist;
1696 	for (i=0;; i++) {
1697 		a=(aMin+aMax)*0.5;
1698 		ap=GetCurvePoint(a);
1699 		tp.X=ap.X+x/exp(ap.Z);
1700 		tp.Z=ap.Z+z;
1701 		if (aMax-aMin<1E-12 || i>=48) break;
1702 		if (tp.X<=0.0) {
1703 			aMin=a;
1704 			continue;
1705 		}
1706 		if (tp.X>=CurvePoints[CurveMaxIndex].X) {
1707 			aMax=a;
1708 			continue;
1709 		}
1710 		bMin=tp.Z;
1711 		bMax=tp.Z+tp.X;
1712 		for (j=0;; j++) {
1713 			b=(bMin+bMax)*0.5;
1714 			bp=GetCurvePoint(b);
1715 			if (bMax-bMin<1E-12 || j>=48) break;
1716 			if (tp.Z>bp.Z) {
1717 				if (tp.X<=bp.X) break;
1718 				bMin=b;
1719 			}
1720 			else {
1721 				if (tp.X>=bp.X) break;
1722 				bMax=b;
1723 			}
1724 		}
1725 		if (tp.Z>bp.Z) aMin=a; else aMax=a;
1726 	}
1727 	bMin=tp.Z;
1728 	bMax=tp.Z+tp.X;
1729 	if (bMin<a+z) bMin=a+z;
1730 	if (bMax<bMin) bMax=bMin;
1731 	for (j=0;; j++) {
1732 		b=(bMin+bMax)*0.5;
1733 		if (bMax-bMin<1E-12 || j>=48) break;
1734 		bp=GetCurvePoint(b);
1735 		if (tp.Z>bp.Z) bMin=b; else bMax=b;
1736 	}
1737 
1738 	if (neg) {
1739 		a=-a;
1740 		b=-b;
1741 	}
1742 	if (swap) {
1743 		*pCurvePos=b;
1744 		*pCurveDist=a-b;
1745 	}
1746 	else {
1747 		*pCurvePos=a;
1748 		*pCurveDist=b-a;
1749 	}
1750 }
1751 
1752 
GetCurvePoint(double d)1753 emVisitingViewAnimator::CurvePoint emVisitingViewAnimator::GetCurvePoint(double d)
1754 {
1755 	double t,x1,z1,x2,z2,x3,z3,c1,c2,c3,dx1,dz1,dx2,dz2;
1756 	CurvePoint cp;
1757 	int i;
1758 
1759 	if (fabs(d)>=CurveMaxIndex*CurveDeltaDist) {
1760 		cp=CurvePoints[CurveMaxIndex];
1761 		if (d<0) cp.X = -cp.X;
1762 		cp.Z+=fabs(d)-CurveMaxIndex*CurveDeltaDist;
1763 		return cp;
1764 	}
1765 
1766 	t=fabs(d)/CurveDeltaDist;
1767 	i=(int)t;
1768 	if (i<0) i=0; // Can happen when d is a nan.
1769 	if (i>=CurveMaxIndex) i=CurveMaxIndex-1;
1770 	t-=i;
1771 	if (t<0.0) t=0.0;
1772 	if (t>1.0) t=1.0;
1773 
1774 	x1=CurvePoints[i].X;
1775 	z1=CurvePoints[i].Z;
1776 	x2=CurvePoints[i+1].X;
1777 	z2=CurvePoints[i+1].Z;
1778 
1779 	if (i<=0) {
1780 		dx1=CurveDeltaDist*0.5;
1781 		dz1=0.0;
1782 	}
1783 	else {
1784 		dx1=(x2-CurvePoints[i-1].X)*0.25;
1785 		dz1=(z2-CurvePoints[i-1].Z)*0.25;
1786 	}
1787 
1788 	if (i+2>CurveMaxIndex) {
1789 		dx2=0.0;
1790 		dz2=CurveDeltaDist*0.5;
1791 	}
1792 	else {
1793 		dx2=(CurvePoints[i+2].X-x1)*0.25;
1794 		dz2=(CurvePoints[i+2].Z-z1)*0.25;
1795 	}
1796 
1797 	x3=(x1+dx1+x2-dx2)*0.5;
1798 	z3=(z1+dz1+z2-dz2)*0.5;
1799 
1800 	c1 = (1.0-t)*(1.0-t);
1801 	c2 = t*t;
1802 	c3 = 2*t*(1.0-t);
1803 	cp.X = x1*c1 + x2*c2 + x3*c3;
1804 	cp.Z = z1*c1 + z2*c2 + z3*c3;
1805 	if (d<0) cp.X = -cp.X;
1806 	return cp;
1807 }
1808 
1809 
1810 const double emVisitingViewAnimator::CurveDeltaDist = 0.0703125;
1811 
1812 
1813 const emVisitingViewAnimator::CurvePoint emVisitingViewAnimator::CurvePoints[]={
1814 	// This table was created with the following program. It's about the
1815 	// shortest way respectively the way of minimum cost between two points
1816 	// in an (x,z) coordinate system, where x is scrolling, and where z is
1817 	// zooming as the natural logarithm of zoom factor, and where moving an
1818 	// infinitesimal step in x has the same cost as moving the same step in
1819 	// z, at z=0. For other z, cost of x has to be multiplied with exp(z).
1820 	// I tried hard but did not find a direct function for the problem. So
1821 	// the program solves it with iterations. I could write book how I came
1822 	// to this (five different iterative algorithms...). If one ever works
1823 	// on this: Be aware of the extreme behavior of the curve when it comes
1824 	// to x near 1 - big risks of calculation errors - always check the
1825 	// results very carefully (even graphical display of derivative...).
1826 	//
1827 	// #include <boost/multiprecision/mpfr.hpp>
1828 	// #include <stdio.h>
1829 	//
1830 	// typedef boost::multiprecision::number<
1831 	//   boost::multiprecision::mpfr_float_backend<50>
1832 	// > FLT;
1833 	//
1834 	// double FLT2DBL(const FLT & x) {
1835 	//   return x.convert_to<double>();
1836 	// }
1837 	//
1838 	// static FLT Cost(FLT x1, FLT z1, FLT x2, FLT z2)
1839 	// {
1840 	//   FLT s = (z2-z1)/(x2-x1);
1841 	//   if (fabs(s)<1E-10) {
1842 	//     return sqrt(pow(z2-z1,2) + pow((x2-x1)*exp((z2+z1)*0.5),2));
1843 	//   }
1844 	//   // Calculate integral from x1 to x2 of:
1845 	//   //   sqrt((s*dx)^2 + (dx*exp(z1+s*(x-x1)))^2)
1846 	//   // Same as:
1847 	//   //   sqrt(s^2 + exp(z1+s*(x-x1))^2) * dx
1848 	//   // Solution:
1849 	//   FLT w1 = sqrt(exp(2*z1) + s*s);
1850 	//   FLT w2 = sqrt(exp(2*(s*(x2-x1)+z1)) + s*s);
1851 	//   FLT c1 = log(w1-s)/2 - log(w1+s)/2 + w1/s;
1852 	//   FLT c2 = log(w2-s)/2 - log(w2+s)/2 + w2/s;
1853 	//   return c2-c1;
1854 	// }
1855 	//
1856 	// int main(int argc, char * argv[])
1857 	// {
1858 	//   const int curveSize=128;
1859 	//   FLT curveX[curveSize];
1860 	//   FLT curveZ[curveSize];
1861 	//
1862 	//   FLT dt=1.0/curveSize*9.0;
1863 	//   FLT x1=0.0;
1864 	//   FLT z1=0.0;
1865 	//   FLT x3=0.0;
1866 	//   FLT z3=0.0;
1867 	//   curveX[0]=0.0;
1868 	//   curveZ[0]=0.0;
1869 	//   for (int i=1; i<curveSize; i++) {
1870 	//     // Calculate (x3,z3) so that:
1871 	//     // - The distance from (x2,z2) to (x3,z3) equals dt.
1872 	//     // - The cheapest way from (x1,z1) to (x3,z3) goes via (x2,z2).
1873 	//     // Where (x2,z2) is (0,0).
1874 	//     FLT minA=0.0;
1875 	//     FLT maxA=acos((FLT)0.0);
1876 	//     for (int j=0; j<128; j++) {
1877 	//       FLT a=(minA+maxA)*0.5;
1878 	//       z3=sin(a)*dt;
1879 	//       if (fabs(z3)>1E-3) {
1880 	//         x3=cos(a)*dt*(1.0-exp(-z3))/z3;
1881 	//       }
1882 	//       else {
1883 	//         x3=cos(a)*dt/exp(z3*0.5);
1884 	//       }
1885 	//       if (i==1) {
1886 	//         x1=-x3;
1887 	//         z1=z3;
1888 	//       }
1889 	//       FLT dx=x3-x1;
1890 	//       FLT dz=z3-z1;
1891 	//       FLT l=sqrt(dx*dx+dz*dz);
1892 	//       FLT q=1E-12;
1893 	//       FLT x2b=-dz/l*q;
1894 	//       FLT z2b=dx/l*q;
1895 	//       FLT cost1 = Cost(x1,z1,0.0,0.0) + Cost(0.0,0.0,x3,z3);
1896 	//       FLT cost2 = Cost(x1,z1,x2b,z2b) + Cost(x2b,z2b,x3,z3);
1897 	//       if (cost2<cost1) maxA=a; else minA=a;
1898 	//     }
1899 	//     curveX[i]=curveX[i-1]+x3/exp(curveZ[i-1]);
1900 	//     curveZ[i]=curveZ[i-1]+z3;
1901 	//     x1=-x3*exp(z3);
1902 	//     z1=-z3;
1903 	//   }
1904 	//
1905 	//   for (int i=0; i<curveSize; i++) {
1906 	//     // A little "correction".
1907 	//     curveX[i]/=curveX[curveSize-1];
1908 	//   }
1909 	//
1910 	//   printf("double CurveDeltaDist = %.8f;\n\n",FLT2DBL(dt));
1911 	//   printf("CurvePoint CurvePoints[]={");
1912 	//   for (int i=0; i<curveSize; i++) {
1913 	//     printf("\n\t{ %.12f, %.8f }",FLT2DBL(curveX[i]),FLT2DBL(curveZ[i]));
1914 	//     if (i<curveSize-1) printf(",");
1915 	//   }
1916 	//   printf("\n};\n");
1917 	//   return 0;
1918 	// }
1919 	{ 0.000000000000, 0.00000000 },
1920 	{ 0.070196996568, 0.00246786 },
1921 	{ 0.139706409829, 0.00984731 },
1922 	{ 0.207867277855, 0.02206685 },
1923 	{ 0.274069721820, 0.03901038 },
1924 	{ 0.337775385698, 0.06052148 },
1925 	{ 0.398532523720, 0.08640897 },
1926 	{ 0.455985066529, 0.11645328 },
1927 	{ 0.509875667166, 0.15041328 },
1928 	{ 0.560043303236, 0.18803304 },
1929 	{ 0.606416423020, 0.22904841 },
1930 	{ 0.649002844597, 0.27319286 },
1931 	{ 0.687877659276, 0.32020270 },
1932 	{ 0.723170290571, 0.36982132 },
1933 	{ 0.755051666347, 0.42180262 },
1934 	{ 0.783722223449, 0.47591350 },
1935 	{ 0.809401221691, 0.53193559 },
1936 	{ 0.832317626300, 0.58966630 },
1937 	{ 0.852702640725, 0.64891924 },
1938 	{ 0.870783840857, 0.70952419 },
1939 	{ 0.886780775044, 0.77132669 },
1940 	{ 0.900901845307, 0.83418737 },
1941 	{ 0.913342265529, 0.89798109 },
1942 	{ 0.924282893555, 0.96259598 },
1943 	{ 0.933889748709, 1.02793239 },
1944 	{ 0.942314048229, 1.09390185 },
1945 	{ 0.949692621183, 1.16042604 },
1946 	{ 0.956148583577, 1.22743579 },
1947 	{ 0.961792181829, 1.29487016 },
1948 	{ 0.966721732461, 1.36267553 },
1949 	{ 0.971024603574, 1.43080482 },
1950 	{ 0.974778198188, 1.49921674 },
1951 	{ 0.978050911290, 1.56787514 },
1952 	{ 0.980903041613, 1.63674836 },
1953 	{ 0.983387646274, 1.70580876 },
1954 	{ 0.985551331675, 1.77503218 },
1955 	{ 0.987434978026, 1.84439754 },
1956 	{ 0.989074397556, 1.91388643 },
1957 	{ 0.990500928466, 1.98348284 },
1958 	{ 0.991741967860, 2.05317279 },
1959 	{ 0.992821447684, 2.12294410 },
1960 	{ 0.993760258098, 2.19278617 },
1961 	{ 0.994576622816, 2.26268978 },
1962 	{ 0.995286430928, 2.33264690 },
1963 	{ 0.995903529539, 2.40265055 },
1964 	{ 0.996439981325, 2.47269463 },
1965 	{ 0.996906290795, 2.54277387 },
1966 	{ 0.997311602787, 2.61288367 },
1967 	{ 0.997663876350, 2.68302002 },
1968 	{ 0.997970036920, 2.75317946 },
1969 	{ 0.998236109346, 2.82335896 },
1970 	{ 0.998467334097, 2.89355590 },
1971 	{ 0.998668268700, 2.96376798 },
1972 	{ 0.998842876218, 3.03399323 },
1973 	{ 0.998994602405, 3.10422992 },
1974 	{ 0.999126442933, 3.17447655 },
1975 	{ 0.999241001961, 3.24473182 },
1976 	{ 0.999340543132, 3.31499459 },
1977 	{ 0.999427033975, 3.38526388 },
1978 	{ 0.999502184543, 3.45553885 },
1979 	{ 0.999567481032, 3.52581873 },
1980 	{ 0.999624215036, 3.59610289 },
1981 	{ 0.999673508983, 3.66639077 },
1982 	{ 0.999716338261, 3.73668188 },
1983 	{ 0.999753550459, 3.80697579 },
1984 	{ 0.999785882091, 3.87727215 },
1985 	{ 0.999813973141, 3.94757062 },
1986 	{ 0.999838379703, 4.01787093 },
1987 	{ 0.999859584970, 4.08817284 },
1988 	{ 0.999878008788, 4.15847614 },
1989 	{ 0.999894015951, 4.22878064 },
1990 	{ 0.999907923421, 4.29908620 },
1991 	{ 0.999920006597, 4.36939266 },
1992 	{ 0.999930504759, 4.43969992 },
1993 	{ 0.999939625809, 4.51000786 },
1994 	{ 0.999947550381, 4.58031641 },
1995 	{ 0.999954435420, 4.65062547 },
1996 	{ 0.999960417283, 4.72093498 },
1997 	{ 0.999965614444, 4.79124489 },
1998 	{ 0.999970129838, 4.86155513 },
1999 	{ 0.999974052897, 4.93186567 },
2000 	{ 0.999977461322, 5.00217647 },
2001 	{ 0.999980422622, 5.07248749 },
2002 	{ 0.999982995451, 5.14279871 },
2003 	{ 0.999985230769, 5.21311009 },
2004 	{ 0.999987172851, 5.28342162 },
2005 	{ 0.999988860164, 5.35373328 },
2006 	{ 0.999990326129, 5.42404505 },
2007 	{ 0.999991599784, 5.49435691 },
2008 	{ 0.999992706355, 5.56466886 },
2009 	{ 0.999993667762, 5.63498088 },
2010 	{ 0.999994503048, 5.70529296 },
2011 	{ 0.999995228757, 5.77560510 },
2012 	{ 0.999995859265, 5.84591728 },
2013 	{ 0.999996407059, 5.91622951 },
2014 	{ 0.999996882992, 5.98654177 },
2015 	{ 0.999997296489, 6.05685406 },
2016 	{ 0.999997655742, 6.12716639 },
2017 	{ 0.999997967867, 6.19747873 },
2018 	{ 0.999998239046, 6.26779109 },
2019 	{ 0.999998474650, 6.33810348 },
2020 	{ 0.999998679346, 6.40841587 },
2021 	{ 0.999998857189, 6.47872829 },
2022 	{ 0.999999011703, 6.54904071 },
2023 	{ 0.999999145946, 6.61935314 },
2024 	{ 0.999999262578, 6.68966558 },
2025 	{ 0.999999363911, 6.75997803 },
2026 	{ 0.999999451949, 6.83029049 },
2027 	{ 0.999999528439, 6.90060295 },
2028 	{ 0.999999594894, 6.97091542 },
2029 	{ 0.999999652632, 7.04122789 },
2030 	{ 0.999999702795, 7.11154036 },
2031 	{ 0.999999746377, 7.18185284 },
2032 	{ 0.999999784242, 7.25216532 },
2033 	{ 0.999999817140, 7.32247781 },
2034 	{ 0.999999845722, 7.39279029 },
2035 	{ 0.999999870554, 7.46310278 },
2036 	{ 0.999999892129, 7.53341527 },
2037 	{ 0.999999910874, 7.60372776 },
2038 	{ 0.999999927159, 7.67404025 },
2039 	{ 0.999999941309, 7.74435274 },
2040 	{ 0.999999953602, 7.81466524 },
2041 	{ 0.999999964282, 7.88497773 },
2042 	{ 0.999999973561, 7.95529023 },
2043 	{ 0.999999981623, 8.02560272 },
2044 	{ 0.999999988627, 8.09591522 },
2045 	{ 0.999999994713, 8.16622772 },
2046 	{ 1.000000000000, 8.23654021 }
2047 };
2048 
2049 
2050 const int emVisitingViewAnimator::CurveMaxIndex=
2051 	sizeof(emVisitingViewAnimator::CurvePoints) /
2052 	sizeof(emVisitingViewAnimator::CurvePoint) - 1
2053 ;
2054