objects.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include "objects.h"
  4. #include "pinball0.h"
  5. #include "graphics.h"
  6. Object::Object(const Vec2& p_, float r_)
  7. : p(p_)
  8. , prev_p(p_)
  9. , a({0.0, 0.0})
  10. , r(r_)
  11. , physical(true)
  12. , bounce(1.0f)
  13. , fixed(false)
  14. , score(0) {
  15. }
  16. void Object::update(float dt) {
  17. if(fixed) {
  18. return;
  19. }
  20. Vec2 velocity = p - prev_p;
  21. // table friction / damping
  22. velocity *= 0.9999f;
  23. prev_p = p;
  24. p = p + velocity + a + (dt * dt);
  25. a = {0.0, 0.0};
  26. }
  27. void Ball::draw(Canvas* canvas) {
  28. gfx_draw_disc(canvas, p, r);
  29. }
  30. Flipper::Flipper(const Vec2& p_, Side side_, size_t size_)
  31. : p(p_)
  32. , side(side_)
  33. , size(size_)
  34. , r(20.0f)
  35. , max_rotation(1.0f)
  36. , omega(4.0f)
  37. , rotation(0.0f)
  38. , powered(false)
  39. , score(50)
  40. , notification(nullptr) {
  41. if(side_ == Side::LEFT) {
  42. rest_angle = -0.4f;
  43. sign = 1;
  44. } else {
  45. rest_angle = M_PI + 0.4;
  46. sign = -1;
  47. }
  48. }
  49. void Flipper::draw(Canvas* canvas) {
  50. // tip
  51. float angle = rest_angle + sign * rotation;
  52. Vec2 dir(cos(angle), -sin(angle));
  53. // draw the tip
  54. Vec2 tip = p + dir * size;
  55. gfx_draw_line_thick(canvas, p, tip, (r * 1.5f) / 10.0f);
  56. gfx_draw_disc(canvas, tip, r * 0.6f);
  57. // // base / pivot
  58. // gfx_draw_circle(canvas, p, r);
  59. // // tip
  60. // float angle = rest_angle + sign * rotation;
  61. // Vec2 dir(cos(angle), -sin(angle));
  62. // // draw the tip
  63. // Vec2 tip = p + dir * size;
  64. // gfx_draw_circle(canvas, tip, r);
  65. // // top and bottom lines
  66. // Vec2 perp(-dir.y, dir.x);
  67. // perp.normalize();
  68. // Vec2 start = p + perp * r;
  69. // Vec2 end = start + dir * size;
  70. // gfx_draw_line(canvas, start, end);
  71. // perp *= -1.0f;
  72. // start = p + perp * r;
  73. // end = start + dir * size;
  74. // gfx_draw_line(canvas, start, end);
  75. }
  76. void Flipper::update(float dt) {
  77. float prev_rotation = rotation;
  78. if(powered) {
  79. rotation = fmin(rotation + dt * omega, max_rotation);
  80. } else {
  81. rotation = fmax(rotation - dt * omega, 0.0f);
  82. }
  83. current_omega = sign * (rotation - prev_rotation) / dt;
  84. }
  85. bool Flipper::collide(Ball& ball) {
  86. Vec2 closest = Vec2_closest(p, get_tip(), ball.p);
  87. Vec2 dir = ball.p - closest;
  88. float dist = dir.mag();
  89. if(dist <= VEC2_EPSILON || dist > ball.r + r) {
  90. return false;
  91. }
  92. dir = dir / dist;
  93. Vec2 ball_v = ball.p - ball.prev_p;
  94. // adjust ball position
  95. float corr = ball.r + r - dist;
  96. ball.p += dir * corr;
  97. closest += dir * r;
  98. closest -= p;
  99. Vec2 perp(-closest.y, closest.x);
  100. perp *= -1.0f;
  101. perp.normalize();
  102. Vec2 surface_velocity = perp * 1.7f; // TODO: flipper power??
  103. // FURI_LOG_I(TAG, "sv: %.3f,%.3f", (double)surface_velocity.x, (double)surface_velocity.y);
  104. if(current_omega != 0.0f) surface_velocity *= current_omega;
  105. // FURI_LOG_I(TAG, "sv: %.3f,%.3f", (double)surface_velocity.x, (double)surface_velocity.y);
  106. // TODO: Flippers currently aren't "bouncy" when they are still
  107. float v = ball_v.dot(dir);
  108. float v_new = surface_velocity.dot(dir);
  109. // FURI_LOG_I(TAG, "v_new: %.4f, v: %.4f", (double)v_new, (double)v);
  110. ball_v += dir * (v_new - v);
  111. ball.prev_p = ball.p - ball_v;
  112. return true;
  113. }
  114. Vec2 Flipper::get_tip() const {
  115. float angle = rest_angle + sign * rotation;
  116. Vec2 dir(cos(angle), -sin(angle));
  117. Vec2 tip = p + dir * size;
  118. return tip;
  119. }
  120. // The default action for receiving a signal is to "appear"
  121. void FixedObject::signal_receive() {
  122. physical = true;
  123. hidden = false;
  124. }
  125. // The default action for a sending signal to have completed is to "hide"
  126. void FixedObject::signal_send() {
  127. physical = false;
  128. hidden = true;
  129. }
  130. void Polygon::draw(Canvas* canvas) {
  131. if(!hidden) {
  132. for(size_t i = 0; i < points.size() - 1; i++) {
  133. gfx_draw_line(canvas, points[i], points[i + 1]);
  134. #ifdef DRAW_NORMALS
  135. Vec2 c = (points[i] + points[i + 1]) / 2.0f;
  136. Vec2 e = c + normals[i] * 40.0f;
  137. gfX_draw_line(canvas, c, e);
  138. #endif
  139. }
  140. }
  141. }
  142. // Attempt to handle double_sided rails better
  143. bool Polygon::collide(Ball& ball) {
  144. Vec2 ball_v = ball.p - ball.prev_p;
  145. Vec2 dir;
  146. Vec2 closest = points[0];
  147. Vec2 normal = normals[0];
  148. float min_dist = infinityf();
  149. for(size_t i = 0; i < points.size() - 1; i++) {
  150. Vec2& p1 = points[i];
  151. Vec2& p2 = points[i + 1];
  152. Vec2 c = Vec2_closest(p1, p2, ball.p);
  153. dir = ball.p - c;
  154. float dist = dir.mag();
  155. if(dist < min_dist) {
  156. min_dist = dist;
  157. closest = c;
  158. normal = normals[i];
  159. }
  160. }
  161. dir = ball.p - closest;
  162. float dist = dir.mag();
  163. if(dist > ball.r) {
  164. return false;
  165. }
  166. if(dist <= VEC2_EPSILON) {
  167. dir = normal;
  168. dist = normal.mag();
  169. }
  170. dir = dir / dist;
  171. if(ball_v.dot(normal) < 0.0f) {
  172. // FURI_LOG_I(TAG, "Collision Moving TOWARDS");
  173. ball.p += dir * (ball.r - dist);
  174. } else {
  175. // TODO: This is key - we're moving away, so don't alter our v / prev_p!
  176. // FURI_LOG_I(TAG, "Collision Moving AWAY");
  177. return false;
  178. // ball.p += dir * -(dist + ball.r);
  179. }
  180. // FURI_LOG_I(
  181. // TAG,
  182. // "p: %.3f,%.3f dir: %.3f,%.3f norm: %.3f,%.3f",
  183. // (double)ball.p.x,
  184. // (double)ball.p.y,
  185. // (double)dir.x,
  186. // (double)dir.y,
  187. // (double)normal.x,
  188. // (double)normal.y);
  189. float v = ball_v.dot(dir);
  190. float v_new = fabs(v) * bounce;
  191. ball_v += dir * (v_new - v);
  192. ball.prev_p = ball.p - ball_v;
  193. return true;
  194. }
  195. void Polygon::finalize() {
  196. if(points.size() < 2) {
  197. FURI_LOG_E(TAG, "Polygon: FINALIZE ERROR - insufficient points");
  198. return;
  199. }
  200. // compute and store normals on all segments
  201. for(size_t i = 0; i < points.size() - 1; i++) {
  202. Vec2 normal(points[i + 1].y - points[i].y, points[i].x - points[i + 1].x);
  203. normal.normalize();
  204. normals.push_back(normal);
  205. }
  206. }
  207. void Portal::draw(Canvas* canvas) {
  208. if(!hidden) {
  209. Vec2 d;
  210. Vec2 e;
  211. // Portal A
  212. gfx_draw_line(canvas, a1, a2);
  213. d = a1 + au * amag * 0.33f;
  214. e = d + na * 20.0f;
  215. gfx_draw_line(canvas, d, e);
  216. d += au * amag * 0.33f;
  217. e = d + na * 20.0f;
  218. gfx_draw_line(canvas, d, e);
  219. // Portal B
  220. gfx_draw_line(canvas, b1, b2);
  221. d = b1 + bu * bmag * 0.33f;
  222. e = d + nb * 20.0f;
  223. gfx_draw_line(canvas, d, e);
  224. d += bu * bmag * 0.33f;
  225. e = d + nb * 20.0f;
  226. gfx_draw_line(canvas, d, e);
  227. if(decay > 0) {
  228. gfx_draw_circle(canvas, enter_p, 20);
  229. }
  230. }
  231. #ifdef DRAW_NORMALS
  232. Vec2 c = (a1 + a2) / 2.0f;
  233. Vec2 e = c + na * 40.0f;
  234. gfx_draw_line(canvas, c, e);
  235. c = (b1 + b2) / 2.0f;
  236. e = c + nb * 40.0f;
  237. gfx_draw_line(canvas, c, e);
  238. #endif
  239. }
  240. // TODO: simplify this code?
  241. bool Portal::collide(Ball& ball) {
  242. Vec2 ball_v = ball.p - ball.prev_p;
  243. float dist;
  244. Vec2 a_cl = Vec2_closest(a1, a2, ball.p);
  245. dist = (ball.p - a_cl).mag();
  246. if(dist <= ball.r && ball_v.dot(na) < 0.0f) {
  247. // entering portal a! move it to portal b
  248. // how far "along" the portal are we?
  249. enter_p = a_cl;
  250. float offset = (a_cl - a1).mag() / amag;
  251. ball.p = b2 - bu * (bmag * offset);
  252. // ensure we're "outside" the next portal to prevent rapid re-entry
  253. ball.p += nb * ball.r;
  254. // get projections on entry portal
  255. float m = -ball_v.dot(au); // tangent magnitude
  256. float n = ball_v.dot(na); // normal magnitude
  257. // FURI_LOG_I(
  258. // TAG,
  259. // "v: %.3f,%.3f u: %.3f,%.3f n: %.3f,%.3f M: %.3f N: %.3f",
  260. // (double)ball_v.x,
  261. // (double)ball_v.y,
  262. // (double)au.x,
  263. // (double)au.y,
  264. // (double)na.x,
  265. // (double)na.y,
  266. // (double)m,
  267. // (double)n);
  268. // transform to exit portal
  269. ball_v.x = bu.x * m - nb.x * n;
  270. ball_v.y = bu.y * m - nb.y * n;
  271. FURI_LOG_I(TAG, "new v: %.3f,%.3f", (double)ball_v.x, (double)ball_v.y);
  272. ball.prev_p = ball.p - ball_v;
  273. return true;
  274. }
  275. Vec2 b_cl = Vec2_closest(b1, b2, ball.p);
  276. dist = (ball.p - b_cl).mag();
  277. if(dist <= ball.r && ball_v.dot(nb) < 0.0f) {
  278. // entering portal b! move it to portal a
  279. // how far "along" the portal are we?
  280. enter_p = b_cl;
  281. float offset = (b_cl - b1).mag() / bmag;
  282. ball.p = a2 - au * (amag * offset);
  283. // ensure we're "outside" the next portal to prevent rapid re-entry
  284. ball.p += na * ball.r;
  285. // get projections on entry portal
  286. float m = -ball_v.dot(bu); // tangent magnitude
  287. float n = ball_v.dot(nb); // normal magnitude
  288. // FURI_LOG_I(
  289. // TAG,
  290. // "v: %.3f,%.3f u: %.3f,%.3f n: %.3f,%.3f M: %.3f N: %.3f",
  291. // (double)ball_v.x,
  292. // (double)ball_v.y,
  293. // (double)bu.x,
  294. // (double)bu.y,
  295. // (double)nb.x,
  296. // (double)nb.y,
  297. // (double)m,
  298. // (double)n);
  299. // transform to exit portal
  300. ball_v.x = au.x * m - na.x * n;
  301. ball_v.y = au.y * m - na.y * n;
  302. FURI_LOG_I(TAG, "new v: %.3f,%.3f", (double)ball_v.x, (double)ball_v.y);
  303. ball.prev_p = ball.p - ball_v;
  304. return true;
  305. }
  306. return false;
  307. }
  308. void Portal::reset_animation() {
  309. decay = 8;
  310. }
  311. void Portal::step_animation() {
  312. if(decay > 0) {
  313. decay--;
  314. } else {
  315. decay = 0;
  316. }
  317. }
  318. void Portal::finalize() {
  319. na = Vec2(a2.y - a1.y, a1.x - a2.x);
  320. na.normalize();
  321. amag = (a2 - a1).mag();
  322. au = (a2 - a1) / amag;
  323. nb = Vec2(b2.y - b1.y, b1.x - b2.x);
  324. nb.normalize();
  325. bmag = (b2 - b1).mag();
  326. bu = (b2 - b1) / bmag;
  327. }
  328. Arc::Arc(const Vec2& p_, float r_, float s_, float e_, Surface surf_)
  329. : FixedObject()
  330. , p(p_)
  331. , r(r_)
  332. , start(s_)
  333. , end(e_)
  334. , surface(surf_) {
  335. // Vec2 s(p.x + r * cosf(start), p.y - r * sinf(start));
  336. // Vec2 e(p.x + r * cosf(end), p.y - r * sinf(end));
  337. // FURI_LOG_I(
  338. // TAG, "ARC: %.2f,%.2f - %.2f,%.2f", (double)s.x, (double)s.y, (double)e.x, (double)e.y);
  339. }
  340. void Arc::draw(Canvas* canvas) {
  341. if(hidden) {
  342. return;
  343. }
  344. if(start == 0 && end == (float)M_PI * 2) {
  345. gfx_draw_circle(canvas, p, r);
  346. } else {
  347. float adj_end = end;
  348. if(end < start) {
  349. adj_end += (float)M_PI * 2;
  350. }
  351. // initialize to start of arc
  352. float sx = p.x + r * cosf(start);
  353. float sy = p.y - r * sinf(start);
  354. size_t segments = r / 8;
  355. for(size_t i = 1; i <= segments; i++) { // for now, use r to determin number of segments
  356. float nx = p.x + r * cosf(start + i / (segments / (adj_end - start)));
  357. float ny = p.y - r * sinf(start + i / (segments / (adj_end - start)));
  358. gfx_draw_line(canvas, sx, sy, nx, ny);
  359. sx = nx;
  360. sy = ny;
  361. }
  362. }
  363. }
  364. // returns value between 0 and 2 PI
  365. // assumes x,y are on cartesean plane, thus you should pass it a neg y
  366. // since the display on flipper is y-inverted
  367. float vector_to_angle(float x, float y) {
  368. if(x == 0) // special cases UP or DOWN
  369. return (y > 0) ? M_PI_2 : (y == 0) ? 0 : M_PI + M_PI_2;
  370. else if(y == 0) // special cases LEFT or RIGHT
  371. return (x >= 0) ? 0 : M_PI;
  372. float ret = atanf(y / x); // quadrant I
  373. if(x < 0 && y < 0) // quadrant III
  374. ret = (float)M_PI + ret;
  375. else if(x < 0) // quadrant II
  376. ret = (float)M_PI + ret; // it actually substracts
  377. else if(y < 0) // quadrant IV
  378. ret = (float)M_PI + (float)M_PI_2 + ((float)M_PI_2 + ret); // it actually substracts
  379. return ret;
  380. }
  381. // Matthias research - 10 minute physics
  382. bool Arc::collide(Ball& ball) {
  383. Vec2 dir = ball.p - p;
  384. float dist = dir.mag();
  385. // FURI_LOG_I(
  386. // TAG,
  387. // "ball.p: %.3f,%.3f p: %.3f,%.3f",
  388. // (double)ball.p.x,
  389. // (double)ball.p.y,
  390. // (double)p.x,
  391. // (double)p.y);
  392. // FURI_LOG_I(TAG, "dir: %.3f,%.3f dist: %.3f", (double)dir.x, (double)dir.y, (double)dist);
  393. if(surface == OUTSIDE) {
  394. if(dist > r + ball.r) {
  395. return false;
  396. }
  397. // FURI_LOG_I(TAG, "hitting arc");
  398. float angle = vector_to_angle(dir.x, -dir.y);
  399. if((start < end && start <= angle && angle <= end) ||
  400. (start > end && (angle >= start || angle <= end))) {
  401. // FURI_LOG_I(TAG, "colliding with arc");
  402. dir.normalize();
  403. Vec2 ball_v = ball.p - ball.prev_p;
  404. float corr = ball.r + r - dist;
  405. ball.p += dir * corr;
  406. float v = ball_v.dot(dir);
  407. ball_v += dir * (3.0f - v); // TODO: pushVel, this should be a prop
  408. ball.prev_p = ball.p - ball_v;
  409. return true;
  410. }
  411. }
  412. if(surface == INSIDE) {
  413. Vec2 prev_dir = ball.prev_p - p;
  414. float prev_dist = prev_dir.mag();
  415. if(prev_dist < r && dist + ball.r > r) {
  416. // FURI_LOG_I(TAG, "Inside an arc!");
  417. float angle = vector_to_angle(dir.x, -dir.y);
  418. // FURI_LOG_I(TAG, "%f : %f : %f", (double)start, (double)angle, (double)end);
  419. // if(angle >= start && angle <= end) {
  420. if((start < end && start <= angle && angle <= end) ||
  421. (start > end && (angle >= start || angle <= end))) {
  422. // FURI_LOG_I(TAG, "Within the arc angle");
  423. dir.normalize();
  424. Vec2 ball_v = ball.p - ball.prev_p;
  425. // correct our position to be "on" the arc
  426. float corr = dist + ball.r - r;
  427. ball.p -= dir * corr;
  428. // Adjust restitution on tangent and normals independently
  429. Vec2 tangent = {-dir.y, dir.x};
  430. float T = (ball_v.x * tangent.x + ball_v.y * tangent.y) * ARC_TANGENT_RESTITUTION;
  431. float N = (ball_v.x * tangent.y - ball_v.y * tangent.x) * ARC_NORMAL_RESTITUTION;
  432. ball_v.x = tangent.x * T - tangent.y * N;
  433. ball_v.y = tangent.y * T + tangent.x * N;
  434. // Current collision - works good, but handles restitution holistically
  435. // float v = ball_v.dot(dir);
  436. // ball_v -= dir * v * 2.0f * bounce;
  437. ball.prev_p = ball.p - ball_v;
  438. return true;
  439. }
  440. }
  441. }
  442. return false;
  443. }
  444. Bumper::Bumper(const Vec2& p_, float r_)
  445. : Arc(p_, r_) {
  446. score = 500;
  447. }
  448. void Bumper::draw(Canvas* canvas) {
  449. Arc::draw(canvas);
  450. if(decay) {
  451. // canvas_draw_disc(canvas, p.x / 10, p.y / 10, (r / 10) * 0.8f * (decay / 30.0f));
  452. gfx_draw_disc(canvas, p, r * 0.8f * (decay / 30.0f));
  453. }
  454. }
  455. void Bumper::reset_animation() {
  456. decay = 30;
  457. }
  458. void Bumper::step_animation() {
  459. if(decay > 20) {
  460. decay--;
  461. } else {
  462. decay = 0;
  463. }
  464. }
  465. void Rollover::draw(Canvas* canvas) {
  466. if(activated) {
  467. canvas_draw_str_aligned(canvas, p.x / 10, p.y / 10, AlignCenter, AlignCenter, c);
  468. } else {
  469. gfx_draw_dot(canvas, p);
  470. }
  471. }
  472. bool Rollover::collide(Ball& ball) {
  473. if(activated) {
  474. return false; // we've already rolled over it, prevent further signals
  475. }
  476. Vec2 dir = ball.p - p;
  477. float dist = dir.mag();
  478. if(dist < 30) {
  479. activated = true;
  480. return true;
  481. }
  482. return false;
  483. }
  484. // Reset the rollover
  485. void Rollover::signal_receive() {
  486. activated = false;
  487. }
  488. void Rollover::signal_send() {
  489. // maybe we should start a blink animation of the letters?
  490. }
  491. void Turbo::draw(Canvas* canvas) {
  492. gfx_draw_line(canvas, chevron_1[0], chevron_1[1]);
  493. gfx_draw_line(canvas, chevron_1[1], chevron_1[2]);
  494. gfx_draw_line(canvas, chevron_2[0], chevron_2[1]);
  495. gfx_draw_line(canvas, chevron_2[1], chevron_2[2]);
  496. }
  497. bool Turbo::collide(Ball& ball) {
  498. float dist = (ball.p - p).mag();
  499. // our distance check doesn't include the ball radius as we want the ball
  500. // to enter the turbo area a bit before being affected by the boost
  501. if(dist < r + 10) {
  502. // apply the turbo in 'dir' with force of 'boost'
  503. ball.prev_p = ball.p - (dir * (boost));
  504. }
  505. return false;
  506. }
  507. Plunger::Plunger(const Vec2& p_)
  508. : Object(p_, 20)
  509. , size(100) {
  510. compression = 0;
  511. }
  512. void Plunger::draw(Canvas* canvas) {
  513. // draw the end / striker
  514. canvas_draw_circle(canvas, p.x / 10, p.y / 10, r / 10);
  515. // draw a line, adjusted for compression
  516. // canvas_draw_line(
  517. // canvas,
  518. // roundf(p.x),
  519. // roundf(p.y),
  520. // roundf(p2.x),
  521. // //roundf(me->p.y - (plunger->size - plunger->compression))
  522. // roundf(p2.y));
  523. }
  524. void Chaser::draw(Canvas* canvas) {
  525. Vec2& p1 = points[0];
  526. Vec2& p2 = points[1];
  527. // TODO: feels like we can do all this with less code?
  528. switch(style) {
  529. case Style::SLASH: // / / / / / / / / /
  530. if(p1.x == p2.x) {
  531. int start = p1.y;
  532. int end = p2.y;
  533. if(start < end) {
  534. for(int y = start + offset; y < end; y += gap) {
  535. canvas_draw_line(canvas, p1.x - 2, y + 2, p1.x + 2, y - 2);
  536. }
  537. } else {
  538. for(int y = start - offset; y > end; y -= gap) {
  539. canvas_draw_line(canvas, p1.x - 2, y + 2, p1.x + 2, y - 2);
  540. }
  541. }
  542. } else if(p1.y == p2.y) {
  543. int start = p1.x;
  544. int end = p2.x;
  545. if(start < end) {
  546. for(int x = start + offset; x < end; x += gap) {
  547. canvas_draw_line(canvas, x - 2, p1.y + 2, x + 2, p1.y - 2);
  548. }
  549. } else {
  550. for(int x = start - offset; x > end; x -= gap) {
  551. canvas_draw_line(canvas, x - 2, p1.y + 2, x + 2, p1.y - 2);
  552. }
  553. }
  554. }
  555. break;
  556. default: // Style::SIMPLE, just dots
  557. // for all pixels between p and q, draw them with offset and gap
  558. if(p1.x == p2.x) {
  559. int start = p1.y;
  560. int end = p2.y;
  561. if(start < end) {
  562. for(int y = start + offset; y < end; y += gap) {
  563. canvas_draw_disc(canvas, p1.x, y, 1);
  564. }
  565. } else {
  566. for(int y = start - offset; y > end; y -= gap) {
  567. canvas_draw_disc(canvas, p1.x, y, 1);
  568. }
  569. }
  570. } else if(p1.y == p2.y) {
  571. int start = p1.x;
  572. int end = p2.x;
  573. if(start < end) {
  574. for(int x = start + offset; x < end; x += gap) {
  575. canvas_draw_disc(canvas, x, p1.y, 1);
  576. }
  577. } else {
  578. for(int x = start - offset; x > end; x -= gap) {
  579. canvas_draw_disc(canvas, x, p1.y, 1);
  580. }
  581. }
  582. }
  583. break;
  584. }
  585. }
  586. void Chaser::step_animation() {
  587. tick++;
  588. if(tick % (speed) == 0) {
  589. offset = (offset + 1) % gap;
  590. }
  591. }