Chris@147
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@147
|
2
|
Chris@147
|
3 /*
|
Chris@147
|
4 Sonic Visualiser
|
Chris@147
|
5 An audio file viewer and annotation editor.
|
Chris@147
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@147
|
7 This file copyright 2006 Chris Cannam.
|
Chris@147
|
8
|
Chris@147
|
9 This program is free software; you can redistribute it and/or
|
Chris@147
|
10 modify it under the terms of the GNU General Public License as
|
Chris@147
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@147
|
12 License, or (at your option) any later version. See the file
|
Chris@147
|
13 COPYING included with this distribution for more information.
|
Chris@147
|
14 */
|
Chris@147
|
15
|
Chris@147
|
16 #include "PowerOfSqrtTwoZoomConstraint.h"
|
Chris@147
|
17
|
Chris@147
|
18 #include <iostream>
|
Chris@147
|
19 #include <cmath>
|
Chris@147
|
20
|
Chris@1528
|
21 #include "base/Debug.h"
|
Chris@1528
|
22
|
Chris@147
|
23
|
Chris@1324
|
24 ZoomLevel
|
Chris@1324
|
25 PowerOfSqrtTwoZoomConstraint::getNearestZoomLevel(ZoomLevel requested,
|
Chris@1429
|
26 RoundingDirection dir) const
|
Chris@147
|
27 {
|
Chris@147
|
28 int type, power;
|
Chris@1550
|
29 double blockSize;
|
Chris@1324
|
30
|
Chris@1324
|
31 if (requested.zone == ZoomLevel::FramesPerPixel) {
|
Chris@1324
|
32 blockSize = getNearestBlockSize(requested.level, type, power, dir);
|
Chris@1324
|
33 return { requested.zone, blockSize };
|
Chris@1324
|
34 } else {
|
Chris@1324
|
35 RoundingDirection opposite = dir;
|
Chris@1324
|
36 if (dir == RoundUp) opposite = RoundDown;
|
Chris@1324
|
37 else if (dir == RoundDown) opposite = RoundUp;
|
Chris@1324
|
38 blockSize = getNearestBlockSize(requested.level, type, power, opposite);
|
Chris@1324
|
39 if (blockSize > getMinZoomLevel().level) {
|
Chris@1324
|
40 blockSize = getMinZoomLevel().level;
|
Chris@1324
|
41 }
|
Chris@1324
|
42 if (blockSize == 1) {
|
Chris@1324
|
43 return { ZoomLevel::FramesPerPixel, 1 };
|
Chris@1324
|
44 } else {
|
Chris@1324
|
45 return { requested.zone, blockSize };
|
Chris@1324
|
46 }
|
Chris@1324
|
47 }
|
Chris@147
|
48 }
|
Chris@147
|
49
|
Chris@1550
|
50 double
|
Chris@1550
|
51 PowerOfSqrtTwoZoomConstraint::getNearestBlockSize(double blockSize,
|
Chris@1429
|
52 int &type,
|
Chris@1429
|
53 int &power,
|
Chris@1429
|
54 RoundingDirection dir) const
|
Chris@147
|
55 {
|
Chris@1528
|
56 // SVCERR << "given " << blockSize << endl;
|
Chris@147
|
57
|
Chris@929
|
58 int minCachePower = getMinCachePower();
|
Chris@147
|
59
|
Chris@1550
|
60 double eps = 1e-8;
|
Chris@1550
|
61
|
Chris@929
|
62 if (blockSize < (1 << minCachePower)) {
|
Chris@1429
|
63 type = -1;
|
Chris@1429
|
64 power = 0;
|
Chris@1550
|
65 double val = 1.0, prevVal = 1.0;
|
Chris@1550
|
66 while (val + eps < blockSize) {
|
Chris@1429
|
67 prevVal = val;
|
Chris@1429
|
68 val *= sqrtf(2.f);
|
Chris@1429
|
69 }
|
Chris@1550
|
70 double rval = val;
|
Chris@1528
|
71 // SVCERR << "got val = " << val << ", rval = " << rval << ", prevVal = " << prevVal << endl;
|
Chris@1528
|
72 if (rval != blockSize && dir != RoundUp) {
|
Chris@1528
|
73 if (dir == RoundDown) {
|
Chris@1550
|
74 rval = prevVal;
|
Chris@1550
|
75 } else if (val - blockSize < blockSize - prevVal) {
|
Chris@1550
|
76 rval = val;
|
Chris@1528
|
77 } else {
|
Chris@1550
|
78 rval = prevVal;
|
Chris@1528
|
79 }
|
Chris@1528
|
80 }
|
Chris@1528
|
81 // SVCERR << "returning " << rval << endl;
|
Chris@1429
|
82 return rval;
|
Chris@147
|
83 }
|
Chris@147
|
84
|
Chris@1550
|
85 double prevBase = (1 << minCachePower);
|
Chris@929
|
86 int prevPower = minCachePower;
|
Chris@929
|
87 int prevType = 0;
|
Chris@147
|
88
|
Chris@1550
|
89 double result = 0;
|
Chris@147
|
90
|
Chris@147
|
91 for (unsigned int i = 0; ; ++i) {
|
Chris@147
|
92
|
Chris@1429
|
93 power = minCachePower + i/2;
|
Chris@1429
|
94 type = i % 2;
|
Chris@147
|
95
|
Chris@1550
|
96 double base;
|
Chris@1429
|
97 if (type == 0) {
|
Chris@1550
|
98 base = pow(2.0, power);
|
Chris@1429
|
99 } else {
|
Chris@1550
|
100 base = sqrt(2.0) * pow(2.0, power);
|
Chris@1429
|
101 }
|
Chris@147
|
102
|
Chris@1528
|
103 // SVCERR << "Testing base " << base << " (i = " << i << ", power = " << power << ", type = " << type << ")" << endl;
|
Chris@377
|
104
|
Chris@377
|
105 if (base == blockSize) {
|
Chris@377
|
106 result = base;
|
Chris@377
|
107 break;
|
Chris@377
|
108 }
|
Chris@377
|
109
|
Chris@1429
|
110 if (base > blockSize) {
|
Chris@1429
|
111 if (dir == RoundNearest) {
|
Chris@1429
|
112 if (base - blockSize < blockSize - prevBase) {
|
Chris@1429
|
113 dir = RoundUp;
|
Chris@1429
|
114 } else {
|
Chris@1429
|
115 dir = RoundDown;
|
Chris@1429
|
116 }
|
Chris@1429
|
117 }
|
Chris@1429
|
118 if (dir == RoundUp) {
|
Chris@1429
|
119 result = base;
|
Chris@1429
|
120 break;
|
Chris@1429
|
121 } else {
|
Chris@1429
|
122 type = prevType;
|
Chris@1429
|
123 power = prevPower;
|
Chris@1429
|
124 result = prevBase;
|
Chris@1429
|
125 break;
|
Chris@1429
|
126 }
|
Chris@1429
|
127 }
|
Chris@147
|
128
|
Chris@1429
|
129 prevType = type;
|
Chris@1429
|
130 prevPower = power;
|
Chris@1429
|
131 prevBase = base;
|
Chris@147
|
132 }
|
Chris@147
|
133
|
Chris@1324
|
134 if (result > getMaxZoomLevel().level) {
|
Chris@1324
|
135 result = getMaxZoomLevel().level;
|
Chris@1324
|
136 }
|
Chris@1324
|
137
|
Chris@147
|
138 return result;
|
Chris@147
|
139 }
|