# Ticket #791: mythstroke.cpp

File mythstroke.cpp, 8.7 KB (added by , 14 years ago) |
---|

Line | |
---|---|

1 | /** |

2 | * @file mythstroke.cpp |

3 | * @author Micah F. Galizia <mfgalizi@csd.uwo.ca> |

4 | * @brief A C++ ripoff of the stroke library, modified for MythTV. |

5 | * |

6 | * Copyright (C) 2005 Micah Galizia |

7 | * |

8 | * This program is free software; you can redistribute it and/or |

9 | * modify it under the terms of the GNU General Public License as |

10 | * published by the Free Software Foundation; either version 2, or (at |

11 | * your option) any later version. |

12 | * |

13 | * This program is distributed in the hope that it will be useful, but |

14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |

15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |

16 | * General Public License for more details. |

17 | * |

18 | * You should have received a copy of the GNU General Public License |

19 | * along with this program; if not, write to the Free Software |

20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |

21 | * 02111-1307, USA |

22 | * |

23 | * This library contains code originally obtained from the libstroke |

24 | * library, which was written by Mark F. Willey. If I am in offense |

25 | * of any copyright or anything, please let me know and I will make |

26 | * the appropriate fixes. |

27 | */ |

28 | #ifndef MYTHSTROKE_CPP |

29 | #define MYTHSTROKE_CPP |

30 | |

31 | using namespace std; |

32 | |

33 | #include "mythstroke.h" |

34 | #include <math.h> |

35 | |

36 | |

37 | MythStroke::MythStroke(size_t max_points, size_t min_points, |

38 | size_t max_sequence, size_t scale_ratio, |

39 | float bin_percent): recording(false), |

40 | min_x(10000), |

41 | max_x(-1), |

42 | min_y(10000), |

43 | max_y(-1), |

44 | max_points(max_points), |

45 | min_points(min_points), |

46 | max_sequence(max_sequence), |

47 | scale_ratio(scale_ratio), |

48 | bin_percent(bin_percent) |

49 | { |

50 | /* Click */ |

51 | sequences.insert("5", MythGestureEvent::Click); |

52 | |

53 | /* Lines */ |

54 | sequences.insert("456", MythGestureEvent::Right); |

55 | sequences.insert("654", MythGestureEvent::Left); |

56 | sequences.insert("258", MythGestureEvent::Down); |

57 | sequences.insert("852", MythGestureEvent::Up); |

58 | |

59 | /* Diagonals */ |

60 | sequences.insert("951", MythGestureEvent::UpLeft); |

61 | sequences.insert("753", MythGestureEvent::UpRight); |

62 | sequences.insert("159", MythGestureEvent::DownRight); |

63 | sequences.insert("357", MythGestureEvent::DownLeft); |

64 | |

65 | /* Double Lines*/ |

66 | sequences.insert("96321",MythGestureEvent::UpThenLeft); |

67 | sequences.insert("74123",MythGestureEvent::UpThenRight); |

68 | sequences.insert("36987",MythGestureEvent::DownThenLeft); |

69 | sequences.insert("14789",MythGestureEvent::DownThenRight); |

70 | sequences.insert("32147",MythGestureEvent::LeftThenDown); |

71 | sequences.insert("98741",MythGestureEvent::LeftThenUp); |

72 | sequences.insert("12369",MythGestureEvent::RightThenDown); |

73 | sequences.insert("78963",MythGestureEvent::RightThenUp); |

74 | |

75 | } |

76 | |

77 | |

78 | |

79 | |

80 | /* comments in header */ |

81 | void MythStroke::adjustExtremes(int x, int y) |

82 | { |

83 | if (x < min_x) min_x = x; |

84 | if (x > max_x) max_x = x; |

85 | if (y < min_y) min_y = y; |

86 | if (y > max_y) max_y = y; |

87 | } |

88 | |

89 | |

90 | |

91 | /* comments in header */ |

92 | int determineBin (const QPoint & p, int x1, int x2, int y1, int y2) |

93 | { |

94 | int bin_num = 1; |

95 | if (p.x() > x1) bin_num += 1; |

96 | if (p.x() > x2) bin_num += 1; |

97 | if (p.y() > y1) bin_num += 3; |

98 | if (p.y() > y2) bin_num += 3; |

99 | |

100 | return bin_num; |

101 | } |

102 | |

103 | |

104 | |

105 | /* comments in header */ |

106 | QString MythStroke::translate(void) |

107 | { |

108 | size_t total_points = points.count(); |

109 | |

110 | if (total_points > max_points) |

111 | { |

112 | points.clear(); |

113 | return "0"; |

114 | } |

115 | |

116 | /* treat any stroke with less than the minimum number of points as |

117 | * a click (not a drag), which is the center bin */ |

118 | if (total_points < min_points) |

119 | { |

120 | points.clear(); |

121 | return "5"; |

122 | } |

123 | |

124 | QString sequence; |

125 | |

126 | /* number of bins recorded in the stroke */ |

127 | size_t sequence_count = 0; |

128 | |

129 | /* points-->sequence translation scratch variables */ |

130 | int prev_bin = 0; |

131 | int current_bin = 0; |

132 | int bin_count = 0; |

133 | |

134 | /*flag indicating the start of a stroke - always count it in the sequence*/ |

135 | bool first_bin = true; |

136 | |

137 | /* bin boundary and size variables */ |

138 | int delta_x, delta_y; |

139 | int bound_x_1, bound_x_2; |

140 | int bound_y_1, bound_y_2; |

141 | |

142 | /* determine size of grid */ |

143 | delta_x = max_x - min_x; |

144 | delta_y = max_y - min_y; |

145 | |

146 | /* calculate bin boundary positions */ |

147 | bound_x_1 = min_x + (delta_x / 3); |

148 | bound_x_2 = min_x + 2 * (delta_x / 3); |

149 | |

150 | bound_y_1 = min_y + (delta_y / 3); |

151 | bound_y_2 = min_y + 2 * (delta_y / 3); |

152 | |

153 | if (delta_x > scale_ratio * delta_y) |

154 | { |

155 | bound_y_1 = (max_y + min_y - delta_x) / 2 + (delta_x / 3); |

156 | bound_y_2 = (max_y + min_y - delta_x) / 2 + 2 * (delta_x / 3); |

157 | } else if (delta_y > scale_ratio * delta_x) |

158 | { |

159 | bound_x_1 = (max_x + min_x - delta_y) / 2 + (delta_y / 3); |

160 | bound_x_2 = (max_x + min_x - delta_y) / 2 + 2 * (delta_y / 3); |

161 | } |

162 | |

163 | /* build string by placing points in bins, collapsing bins and |

164 | discarding those with too few points... */ |

165 | |

166 | while (!points.empty()) |

167 | { |

168 | |

169 | QPoint p = points.front(); |

170 | points.pop_front(); |

171 | |

172 | /* figure out which bin the point falls in */ |

173 | current_bin = determineBin(p, bound_x_1, bound_x_2, bound_y_1, |

174 | bound_y_2); |

175 | |

176 | /* if this is the first point, consider it the previous bin, too. */ |

177 | prev_bin = (prev_bin == 0) ? current_bin : prev_bin; |

178 | |

179 | if (prev_bin == current_bin) bin_count++; |

180 | else { |

181 | |

182 | /* we are moving to a new bin -- consider adding to the |

183 | sequence */ |

184 | if ((bin_count > (total_points * bin_percent)) || first_bin) |

185 | { |

186 | first_bin = false; |

187 | sequence += '0' + prev_bin; |

188 | sequence_count ++; |

189 | } |

190 | |

191 | /* restart counting points in the new bin */ |

192 | bin_count = 0; |

193 | prev_bin = current_bin; |

194 | } |

195 | } |

196 | |

197 | /* add the last run of points to the sequence */ |

198 | sequence += '0' + current_bin; |

199 | sequence_count++; |

200 | |

201 | /* bail out on error cases */ |

202 | if (sequence_count > max_sequence) sequence = "0"; |

203 | |

204 | return sequence; |

205 | } |

206 | |

207 | |

208 | |

209 | /* comments in header */ |

210 | bool MythStroke::record(const QPoint & p) |

211 | { |

212 | /* only record if we haven't exceeded the maximum points */ |

213 | if ((points.size() >= max_points) || !recording) |

214 | return false; |

215 | |

216 | |

217 | if (points.size() == 0) |

218 | { |

219 | points.push_back(p); |

220 | return true; |

221 | } |

222 | |

223 | /* interpolate between last and current point */ |

224 | int delx = p.x() - points.back().x(); |

225 | int dely = p.y() - points.back().y(); |

226 | |

227 | /* step by the greatest delta direction */ |

228 | if (abs(delx) > abs(dely)) |

229 | { |

230 | float iy = points.back().y(); |

231 | |

232 | /* go from the last point to the current, whatever direction |

233 | * it may be */ |

234 | for (float ix = points.back().x(); |

235 | (delx > 0) ? (ix < p.x()) : (ix > p.x()); |

236 | ix += (delx > 0) ? 1 : -1) |

237 | { |

238 | /* step the other axis by the correct increment */ |

239 | iy += fabs(((float) dely / (float) delx)) |

240 | * (float) ((dely < 0) ? -1.0 : 1.0); |

241 | |

242 | points.push_back(QPoint((int)ix, (int)iy)); |

243 | |

244 | adjustExtremes((int)ix, (int)iy); |

245 | } |

246 | } |

247 | else /* same thing, but for dely larger than delx case... */ |

248 | { |

249 | float ix = points.back().x(); |

250 | |

251 | /* go from the last point to the current, whatever direction |

252 | it may be */ |

253 | for (float iy = points.back().y(); |

254 | (dely > 0) ? (iy < p.y()) : (iy > p.y()); |

255 | iy += (dely > 0) ? 1 : -1) |

256 | { |

257 | /* step the other axis by the correct increment */ |

258 | ix += fabs(((float) delx / (float) dely)) |

259 | * (float) ((delx < 0) ? -1.0 : 1.0); |

260 | |

261 | /* add the interpolated point */ |

262 | points.push_back(QPoint((int)ix, (int)iy)); |

263 | |

264 | adjustExtremes((int)ix, (int)iy); |

265 | } |

266 | } |

267 | |

268 | points.push_back(p); |

269 | |

270 | return true; |

271 | } |

272 | |

273 | |

274 | static char *gesturename[] = { |

275 | "Unknown", |

276 | "Click", |

277 | "Up", |

278 | "Down", |

279 | "Left", |

280 | "Right", |

281 | "UpLeft", |

282 | "UpRight", |

283 | "DownLeft", |

284 | "DownRight", |

285 | "UpThenLeft", |

286 | "UpThenRight", |

287 | "DownThenLeft", |

288 | "DownThenRight", |

289 | "LeftThenUp", |

290 | "LeftThenDown", |

291 | "RightThenUp", |

292 | "RightThenDown", |

293 | "MaxGesture" |

294 | }; |

295 | |

296 | |

297 | |

298 | |

299 | MythGestureEvent *MythStroke::complete() |

300 | { |

301 | QString seq; |

302 | this->recording = false; |

303 | seq = translate(); |

304 | |

305 | min_x = min_y = 10000; |

306 | max_x = max_y = -1; |

307 | |

308 | return new MythGestureEvent(sequences[seq]); |

309 | } |

310 | |

311 | |

312 | |

313 | |

314 | /* comments in header */ |

315 | MythGestureEvent::operator QString() const |

316 | { |

317 | return gesturename[_gesture]; |

318 | } |

319 | |

320 | |

321 | |

322 | #endif /* MYTHSTROKE_CPP */ |