| 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 } |