v1.1.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Groups Pages
util.h
1 /* -*- mode: C; c-basic-offset: 4; intent-tabs-mode: nil -*-
2  *
3  * This file is part of the public interface to the Sifteo SDK.
4  * Copyright <c> 2012 Sifteo, Inc. All rights reserved.
5  */
6 
7 #pragma once
8 #ifdef NOT_USERSPACE
9 # error This is a userspace-only header, not allowed by the current build.
10 #endif
11 
12 #include <sifteo/menu/types.h>
13 
14 namespace Sifteo {
15 
21 inline void Menu::detectNeighbors()
22 {
23  Neighborhood nbr(vid->cube());
24 
25  for (Side i = Side(0); i < NUM_SIDES; i++) {
26  MenuNeighbor n;
27 
28  if (nbr.hasCubeAt(i)) {
29  CubeID c(nbr.cubeAt(i));
30  n.neighborSide = Neighborhood(c).sideOf(vid->cube());
31  n.neighbor = c;
32  n.masterSide = i;
33  } else {
34  n.neighborSide = NO_SIDE;
35  n.neighbor = CubeID::UNDEFINED;
36  n.masterSide = NO_SIDE;
37  }
38 
39  if (n != neighbors[i]) {
40  // Neighbor was set but is now different/gone
41  if (neighbors[i].neighbor != CubeID::UNDEFINED) {
42  // Generate a lost neighbor event, with the ghost of the lost cube.
43  currentEvent.type = MENU_NEIGHBOR_REMOVE;
44  currentEvent.neighbor = neighbors[i];
45 
46  /* Act as if the neighbor was lost, even if it wasn't. We'll
47  * synthesize another event on the next run of the event loop
48  * if the neighbor was replaced by another cube in less than
49  * one polling cycle.
50  */
51  neighbors[i].neighborSide = NO_SIDE;
52  neighbors[i].neighbor = CubeID::UNDEFINED;
53  neighbors[i].masterSide = NO_SIDE;
54  } else {
55  neighbors[i] = n;
56  currentEvent.type = MENU_NEIGHBOR_ADD;
57  currentEvent.neighbor = n;
58  }
59 
60  /* We don't have a method of queueing events, so return as soon as
61  * a change is discovered. If there are other changes, the next run
62  * of the event loop will discover them.
63  */
64  return;
65  }
66  }
67 }
68 
69 inline uint8_t Menu::computeSelected()
70 {
71  int s = (position + (kItemPixelWidth() / 2)) / kItemPixelWidth();
72  return clamp(s, 0, numItems - 1);
73 }
74 
75 inline void Menu::checkForPress()
76 {
77  bool touch = vid->cube().isTouching();
78 
79  if (touch && !prevTouch) {
80  currentEvent.type = MENU_ITEM_PRESS;
81  currentEvent.item = computeSelected();
82  }
83  prevTouch = touch;
84 }
85 
86 // positions are in pixel units
87 // columns are in tile-units
88 // each icon is 80px/10tl wide with a 16px/2tl spacing
89 inline void Menu::drawColumn(int x)
90 {
91  // x is the column in "global" space
92  Int2 topLeft = { umod(x, kNumTilesX), 0 };
93  Int2 size = { 1, kIconTileHeight };
94 
95  // icon or blank column?
96  if (itemAtCol(x) < numItems) {
97  // drawing an icon column
98  const AssetImage &img = *items[itemAtCol(x)].icon;
99  Int2 srcXY = { ((x % kItemTileWidth()) % img.tileWidth()), 0 };
100  vid->bg0.image(topLeft, size, img, srcXY);
101  } else {
102  if (assets->overflowIcon && umod(x,kIconTileWidth) < kItemTileWidth()) {
103  // drawing the overflow icon
104  x = umod(x, kItemTileWidth());
105  const AssetImage &img = *assets->overflowIcon;
106  Int2 srcXY = { ((x % kItemTileWidth()) % img.tileWidth()), 0 };
107  vid->bg0.image(topLeft, size, img, srcXY);
108  } else {
109  // drawing a blank column
110  vid->bg0.fill(topLeft, size, *assets->background);
111  }
112  }
113 }
114 
115 inline void Menu::drawFooter(bool force)
116 {
117  if (numTips == 0) return;
118 
119  const AssetImage& footer = *assets->tips[currentTip];
120  const float kSecondsPerTip = 4.f;
121 
122  if (SystemTime::now() - prevTipTime > kSecondsPerTip || force) {
123  prevTipTime = SystemTime::now();
124 
125  if (numTips > 0) {
126  currentTip = (currentTip+1) % numTips;
127  }
128 
129  Int2 topLeft = { 0, kNumVisibleTilesY - footer.tileHeight() };
130  vid->bg1.image(topLeft, footer);
131  }
132 }
133 
134 inline int Menu::stoppingPositionFor(int selected)
135 {
136  return kItemPixelWidth() * selected;
137 }
138 
139 inline float Menu::velocityMultiplier()
140 {
141  return abs(accel.x) > kAccelThresholdStep ? (1.f * kMaxSpeedMultiplier) : 1.f;
142 }
143 
144 inline float Menu::maxVelocity()
145 {
146  const float kMaxNormalSpeed = 40.f;
147  return kMaxNormalSpeed *
148  // x-axis linear limit
149  (abs(accel.x) / kOneG()) *
150  // y-axis multiplier
151  velocityMultiplier();
152 }
153 
154 inline float Menu::lerp(float min, float max, float u)
155 {
156  return min + u * (max - min);
157 }
158 
159 inline void Menu::updateBG0()
160 {
161  int ut = computeCurrentTile();
162 
163  while(prev_ut < ut) {
164  drawColumn(++prev_ut + kNumVisibleTilesX);
165  }
166  while(prev_ut > ut) {
167  drawColumn(--prev_ut);
168  }
169 
170  {
171  Int2 vec = {(position - kEndCapPadding), kIconYOffset};
172  vid->bg0.setPanning(vec);
173  }
174 }
175 
176 inline bool Menu::itemVisibleAtCol(uint8_t item, int column)
177 {
178  ASSERT(item >= 0 && item < numItems);
179  if (column < 0) return false;
180 
181  return itemAtCol(column) == item;
182 }
183 
184 inline uint8_t Menu::itemAtCol(int column)
185 {
186  if (column < 0) return numItems;
187 
188  if (column % kItemTileWidth() < kIconTileWidth) {
189  return column < 0 ? numItems : column / kItemTileWidth();
190  }
191  return numItems;
192 }
193 
194 inline int Menu::computeCurrentTile()
195 {
196  /* these are necessary if the icon widths are an odd number of tiles,
197  * because the position is no longer aligned with the tile domain.
198  */
199 
200  const int kPixelsPerTile = TILE;
201  const int kPositionAlignment = kEndCapPadding % kPixelsPerTile == 0
202  ? 0 : kPixelsPerTile - kEndCapPadding % kPixelsPerTile;
203  const int kTilePadding = kEndCapPadding / kPixelsPerTile;
204 
205  int ui = position - kPositionAlignment;
206  int ut = (ui < 0 ? ui - kPixelsPerTile : ui) / kPixelsPerTile; // special case because int rounds up when < 0
207  ut -= kTilePadding;
208 
209  return ut;
210 }
211 
216 }; // namespace Sifteo