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