v1.1.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
util.h
1 /* -*- mode: C; c-basic-offset: 4; intent-tabs-mode: nil -*-
2  *
3  * Sifteo SDK
4  *
5  * Copyright <c> 2012 Sifteo, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #pragma once
27 #ifdef NOT_USERSPACE
28 # error This is a userspace-only header, not allowed by the current build.
29 #endif
30 
31 #include <sifteo/menu/types.h>
32 
33 namespace Sifteo {
34 
40 inline void Menu::detectNeighbors()
41 {
42  Neighborhood nbr(vid->cube());
43 
44  for (Side i = Side(0); i < NUM_SIDES; i++) {
45  MenuNeighbor n;
46 
47  if (nbr.hasCubeAt(i)) {
48  CubeID c(nbr.cubeAt(i));
49  n.neighborSide = Neighborhood(c).sideOf(vid->cube());
50  n.neighbor = c;
51  n.masterSide = i;
52  } else {
53  n.neighborSide = NO_SIDE;
54  n.neighbor = CubeID::UNDEFINED;
55  n.masterSide = NO_SIDE;
56  }
57 
58  if (n != neighbors[i]) {
59  // Neighbor was set but is now different/gone
60  if (neighbors[i].neighbor != CubeID::UNDEFINED) {
61  // Generate a lost neighbor event, with the ghost of the lost cube.
62  currentEvent.type = MENU_NEIGHBOR_REMOVE;
63  currentEvent.neighbor = neighbors[i];
64 
65  /* Act as if the neighbor was lost, even if it wasn't. We'll
66  * synthesize another event on the next run of the event loop
67  * if the neighbor was replaced by another cube in less than
68  * one polling cycle.
69  */
70  neighbors[i].neighborSide = NO_SIDE;
71  neighbors[i].neighbor = CubeID::UNDEFINED;
72  neighbors[i].masterSide = NO_SIDE;
73  } else {
74  neighbors[i] = n;
75  currentEvent.type = MENU_NEIGHBOR_ADD;
76  currentEvent.neighbor = n;
77  }
78 
79  /* We don't have a method of queueing events, so return as soon as
80  * a change is discovered. If there are other changes, the next run
81  * of the event loop will discover them.
82  */
83  return;
84  }
85  }
86 }
87 
88 inline uint8_t Menu::computeSelected()
89 {
90  int s = (position + (kItemPixelWidth() / 2)) / kItemPixelWidth();
91  return clamp(s, 0, numItems - 1);
92 }
93 
94 inline void Menu::checkForPress()
95 {
96  bool touch = vid->cube().isTouching();
97 
98  if (touch && !prevTouch) {
99  currentEvent.type = MENU_ITEM_PRESS;
100  currentEvent.item = computeSelected();
101  }
102  prevTouch = touch;
103 }
104 
105 // positions are in pixel units
106 // columns are in tile-units
107 // each icon is 80px/10tl wide with a 16px/2tl spacing
108 inline void Menu::drawColumn(int x)
109 {
110  // x is the column in "global" space
111  Int2 topLeft = { umod(x, kNumTilesX), 0 };
112  Int2 size = { 1, kIconTileHeight };
113 
114  // icon or blank column?
115  if (itemAtCol(x) < numItems) {
116  // drawing an icon column
117  const AssetImage &img = *items[itemAtCol(x)].icon;
118  Int2 srcXY = { ((x % kItemTileWidth()) % img.tileWidth()), 0 };
119  vid->bg0.image(topLeft, size, img, srcXY);
120  } else {
121  if (assets->overflowIcon && umod(x,kIconTileWidth) < kItemTileWidth()) {
122  // drawing the overflow icon
123  x = umod(x, kItemTileWidth());
124  const AssetImage &img = *assets->overflowIcon;
125  Int2 srcXY = { ((x % kItemTileWidth()) % img.tileWidth()), 0 };
126  vid->bg0.image(topLeft, size, img, srcXY);
127  } else {
128  // drawing a blank column
129  vid->bg0.fill(topLeft, size, *assets->background);
130  }
131  }
132 }
133 
134 inline void Menu::drawFooter(bool force)
135 {
136  if (numTips == 0) return;
137 
138  const AssetImage& footer = *assets->tips[currentTip];
139  const float kSecondsPerTip = 4.f;
140 
141  if (SystemTime::now() - prevTipTime > kSecondsPerTip || force) {
142  prevTipTime = SystemTime::now();
143 
144  if (numTips > 0) {
145  currentTip = (currentTip+1) % numTips;
146  }
147 
148  Int2 topLeft = { 0, kNumVisibleTilesY - footer.tileHeight() };
149  vid->bg1.image(topLeft, footer);
150  }
151 }
152 
153 inline int Menu::stoppingPositionFor(int selected)
154 {
155  return kItemPixelWidth() * selected;
156 }
157 
158 inline float Menu::velocityMultiplier()
159 {
160  return abs(accel.x) > kAccelThresholdStep ? (1.f * kMaxSpeedMultiplier) : 1.f;
161 }
162 
163 inline float Menu::maxVelocity()
164 {
165  const float kMaxNormalSpeed = 40.f;
166  return kMaxNormalSpeed *
167  // x-axis linear limit
168  (abs(accel.x) / kOneG()) *
169  // y-axis multiplier
170  velocityMultiplier();
171 }
172 
173 inline float Menu::lerp(float min, float max, float u)
174 {
175  return min + u * (max - min);
176 }
177 
178 inline void Menu::updateBG0()
179 {
180  int ut = computeCurrentTile();
181 
182  while(prev_ut < ut) {
183  drawColumn(++prev_ut + kNumVisibleTilesX);
184  }
185  while(prev_ut > ut) {
186  drawColumn(--prev_ut);
187  }
188 
189  {
190  Int2 vec = {(position - kEndCapPadding), kIconYOffset};
191  vid->bg0.setPanning(vec);
192  }
193 }
194 
195 inline bool Menu::itemVisibleAtCol(uint8_t item, int column)
196 {
197  ASSERT(item >= 0 && item < numItems);
198  if (column < 0) return false;
199 
200  return itemAtCol(column) == item;
201 }
202 
203 inline uint8_t Menu::itemAtCol(int column)
204 {
205  if (column < 0) return numItems;
206 
207  if (column % kItemTileWidth() < kIconTileWidth) {
208  return column < 0 ? numItems : column / kItemTileWidth();
209  }
210  return numItems;
211 }
212 
213 inline int Menu::computeCurrentTile()
214 {
215  /* these are necessary if the icon widths are an odd number of tiles,
216  * because the position is no longer aligned with the tile domain.
217  */
218 
219  const int kPixelsPerTile = TILE;
220  const int kPositionAlignment = kEndCapPadding % kPixelsPerTile == 0
221  ? 0 : kPixelsPerTile - kEndCapPadding % kPixelsPerTile;
222  const int kTilePadding = kEndCapPadding / kPixelsPerTile;
223 
224  int ui = position - kPositionAlignment;
225  int ut = (ui < 0 ? ui - kPixelsPerTile : ui) / kPixelsPerTile; // special case because int rounds up when < 0
226  ut -= kTilePadding;
227 
228  return ut;
229 }
230 
235 }; // namespace Sifteo
static SystemTime now()
Returns a new SystemTime representing the current system clock value.
Definition: time.h:269
#define ASSERT(_x)
Runtime debug assertion.
Definition: macros.h:205
Side
An enumeration which names the four sides of a Sifteo cube.
Definition: cube.h:54
Vector2< int > Int2
Typedef for a 2-vector of ints.
Definition: math.h:641
T abs(const T &value)
For any type, return the absolute value.
Definition: math.h:90
Nil value (-1)
Definition: cube.h:60
Definition: array.h:34
unsigned umod(int a, int b)
Compute the unsigned remainder from dividing two signed integers.
Definition: math.h:208
Total number of sides (4)
Definition: cube.h:59
T clamp(const T &value, const T &low, const T &high)
For any type, clamp a value to the extremes 'low' and 'high'.
Definition: math.h:72
static const _SYSCubeID UNDEFINED
A reserved ID, used to mark undefined CubeIDs.
Definition: cube.h:92
Vector2< T > vec(T x, T y)
Create a Vector2, from a set of (x,y) coordinates.
Definition: math.h:658