objects.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  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 FixedObject::save_state() {
  131. saved.physical = physical;
  132. saved.hidden = hidden;
  133. }
  134. void FixedObject::reset_state() {
  135. physical = saved.physical;
  136. hidden = saved.hidden;
  137. }
  138. void Polygon::draw(Canvas* canvas) {
  139. if(!hidden) {
  140. for(size_t i = 0; i < points.size() - 1; i++) {
  141. gfx_draw_line(canvas, points[i], points[i + 1]);
  142. #ifdef DRAW_NORMALS
  143. Vec2 c = (points[i] + points[i + 1]) / 2.0f;
  144. Vec2 e = c + normals[i] * 40.0f;
  145. gfX_draw_line(canvas, c, e);
  146. #endif
  147. }
  148. }
  149. }
  150. // Attempt to handle double_sided rails better
  151. bool Polygon::collide(Ball& ball) {
  152. Vec2 ball_v = ball.p - ball.prev_p;
  153. Vec2 dir;
  154. Vec2 closest = points[0];
  155. Vec2 normal = normals[0];
  156. float min_dist = infinityf();
  157. for(size_t i = 0; i < points.size() - 1; i++) {
  158. Vec2& p1 = points[i];
  159. Vec2& p2 = points[i + 1];
  160. Vec2 c = Vec2_closest(p1, p2, ball.p);
  161. dir = ball.p - c;
  162. float dist = dir.mag();
  163. if(dist < min_dist) {
  164. min_dist = dist;
  165. closest = c;
  166. normal = normals[i];
  167. }
  168. }
  169. dir = ball.p - closest;
  170. float dist = dir.mag();
  171. if(dist > ball.r) {
  172. return false;
  173. }
  174. if(dist <= VEC2_EPSILON) {
  175. dir = normal;
  176. dist = normal.mag();
  177. }
  178. dir = dir / dist;
  179. if(ball_v.dot(normal) < 0.0f) {
  180. // FURI_LOG_I(TAG, "Collision Moving TOWARDS");
  181. ball.p += dir * (ball.r - dist);
  182. } else {
  183. // TODO: This is key - we're moving away, so don't alter our v / prev_p!
  184. // FURI_LOG_I(TAG, "Collision Moving AWAY");
  185. return false;
  186. // ball.p += dir * -(dist + ball.r);
  187. }
  188. // FURI_LOG_I(
  189. // TAG,
  190. // "p: %.3f,%.3f dir: %.3f,%.3f norm: %.3f,%.3f",
  191. // (double)ball.p.x,
  192. // (double)ball.p.y,
  193. // (double)dir.x,
  194. // (double)dir.y,
  195. // (double)normal.x,
  196. // (double)normal.y);
  197. float v = ball_v.dot(dir);
  198. float v_new = fabs(v) * bounce;
  199. ball_v += dir * (v_new - v);
  200. ball.prev_p = ball.p - ball_v;
  201. return true;
  202. }
  203. void Polygon::finalize() {
  204. if(points.size() < 2) {
  205. FURI_LOG_E(TAG, "Polygon: FINALIZE ERROR - insufficient points");
  206. return;
  207. }
  208. // compute and store normals on all segments
  209. for(size_t i = 0; i < points.size() - 1; i++) {
  210. Vec2 normal(points[i + 1].y - points[i].y, points[i].x - points[i + 1].x);
  211. normal.normalize();
  212. normals.push_back(normal);
  213. }
  214. }
  215. void Portal::draw(Canvas* canvas) {
  216. if(!hidden) {
  217. Vec2 d;
  218. Vec2 e;
  219. // Portal A
  220. gfx_draw_line(canvas, a1, a2);
  221. d = a1 + au * amag * 0.33f;
  222. e = d + na * 20.0f;
  223. gfx_draw_line(canvas, d, e);
  224. d += au * amag * 0.33f;
  225. e = d + na * 20.0f;
  226. gfx_draw_line(canvas, d, e);
  227. // Portal B
  228. gfx_draw_line(canvas, b1, b2);
  229. d = b1 + bu * bmag * 0.33f;
  230. e = d + nb * 20.0f;
  231. gfx_draw_line(canvas, d, e);
  232. d += bu * bmag * 0.33f;
  233. e = d + nb * 20.0f;
  234. gfx_draw_line(canvas, d, e);
  235. if(decay > 0) {
  236. gfx_draw_circle(canvas, enter_p, 20);
  237. }
  238. }
  239. #ifdef DRAW_NORMALS
  240. Vec2 c = (a1 + a2) / 2.0f;
  241. Vec2 e = c + na * 40.0f;
  242. gfx_draw_line(canvas, c, e);
  243. c = (b1 + b2) / 2.0f;
  244. e = c + nb * 40.0f;
  245. gfx_draw_line(canvas, c, e);
  246. #endif
  247. }
  248. // TODO: simplify this code?
  249. bool Portal::collide(Ball& ball) {
  250. Vec2 ball_v = ball.p - ball.prev_p;
  251. float dist;
  252. Vec2 a_cl = Vec2_closest(a1, a2, ball.p);
  253. dist = (ball.p - a_cl).mag();
  254. if(dist <= ball.r && ball_v.dot(na) < 0.0f) {
  255. // entering portal a! move it to portal b
  256. // how far "along" the portal are we?
  257. enter_p = a_cl;
  258. float offset = (a_cl - a1).mag() / amag;
  259. ball.p = b2 - bu * (bmag * offset);
  260. // ensure we're "outside" the next portal to prevent rapid re-entry
  261. ball.p += nb * ball.r;
  262. // get projections on entry portal
  263. float m = -ball_v.dot(au); // tangent magnitude
  264. float n = ball_v.dot(na); // normal magnitude
  265. // FURI_LOG_I(
  266. // TAG,
  267. // "v: %.3f,%.3f u: %.3f,%.3f n: %.3f,%.3f M: %.3f N: %.3f",
  268. // (double)ball_v.x,
  269. // (double)ball_v.y,
  270. // (double)au.x,
  271. // (double)au.y,
  272. // (double)na.x,
  273. // (double)na.y,
  274. // (double)m,
  275. // (double)n);
  276. // transform to exit portal
  277. ball_v.x = bu.x * m - nb.x * n;
  278. ball_v.y = bu.y * m - nb.y * n;
  279. FURI_LOG_I(TAG, "new v: %.3f,%.3f", (double)ball_v.x, (double)ball_v.y);
  280. ball.prev_p = ball.p - ball_v;
  281. return true;
  282. }
  283. Vec2 b_cl = Vec2_closest(b1, b2, ball.p);
  284. dist = (ball.p - b_cl).mag();
  285. if(dist <= ball.r && ball_v.dot(nb) < 0.0f) {
  286. // entering portal b! move it to portal a
  287. // how far "along" the portal are we?
  288. enter_p = b_cl;
  289. float offset = (b_cl - b1).mag() / bmag;
  290. ball.p = a2 - au * (amag * offset);
  291. // ensure we're "outside" the next portal to prevent rapid re-entry
  292. ball.p += na * ball.r;
  293. // get projections on entry portal
  294. float m = -ball_v.dot(bu); // tangent magnitude
  295. float n = ball_v.dot(nb); // normal magnitude
  296. // FURI_LOG_I(
  297. // TAG,
  298. // "v: %.3f,%.3f u: %.3f,%.3f n: %.3f,%.3f M: %.3f N: %.3f",
  299. // (double)ball_v.x,
  300. // (double)ball_v.y,
  301. // (double)bu.x,
  302. // (double)bu.y,
  303. // (double)nb.x,
  304. // (double)nb.y,
  305. // (double)m,
  306. // (double)n);
  307. // transform to exit portal
  308. ball_v.x = au.x * m - na.x * n;
  309. ball_v.y = au.y * m - na.y * n;
  310. FURI_LOG_I(TAG, "new v: %.3f,%.3f", (double)ball_v.x, (double)ball_v.y);
  311. ball.prev_p = ball.p - ball_v;
  312. return true;
  313. }
  314. return false;
  315. }
  316. void Portal::reset_animation() {
  317. decay = 8;
  318. }
  319. void Portal::step_animation() {
  320. if(decay > 0) {
  321. decay--;
  322. } else {
  323. decay = 0;
  324. }
  325. }
  326. void Portal::finalize() {
  327. na = Vec2(a2.y - a1.y, a1.x - a2.x);
  328. na.normalize();
  329. amag = (a2 - a1).mag();
  330. au = (a2 - a1) / amag;
  331. nb = Vec2(b2.y - b1.y, b1.x - b2.x);
  332. nb.normalize();
  333. bmag = (b2 - b1).mag();
  334. bu = (b2 - b1) / bmag;
  335. }
  336. Arc::Arc(const Vec2& p_, float r_, float s_, float e_, Surface surf_)
  337. : FixedObject()
  338. , p(p_)
  339. , r(r_)
  340. , start(s_)
  341. , end(e_)
  342. , surface(surf_) {
  343. // Vec2 s(p.x + r * cosf(start), p.y - r * sinf(start));
  344. // Vec2 e(p.x + r * cosf(end), p.y - r * sinf(end));
  345. // FURI_LOG_I(
  346. // TAG, "ARC: %.2f,%.2f - %.2f,%.2f", (double)s.x, (double)s.y, (double)e.x, (double)e.y);
  347. }
  348. void Arc::draw(Canvas* canvas) {
  349. if(hidden) {
  350. return;
  351. }
  352. if(start == 0 && end == (float)M_PI * 2) {
  353. gfx_draw_circle(canvas, p, r);
  354. } else {
  355. float adj_end = end;
  356. if(end < start) {
  357. adj_end += (float)M_PI * 2;
  358. }
  359. // initialize to start of arc
  360. float sx = p.x + r * cosf(start);
  361. float sy = p.y - r * sinf(start);
  362. size_t segments = r / 8;
  363. for(size_t i = 1; i <= segments; i++) { // for now, use r to determin number of segments
  364. float nx = p.x + r * cosf(start + i / (segments / (adj_end - start)));
  365. float ny = p.y - r * sinf(start + i / (segments / (adj_end - start)));
  366. gfx_draw_line(canvas, sx, sy, nx, ny);
  367. sx = nx;
  368. sy = ny;
  369. }
  370. }
  371. }
  372. // returns value between 0 and 2 PI
  373. // assumes x,y are on cartesean plane, thus you should pass it a neg y
  374. // since the display on flipper is y-inverted
  375. float vector_to_angle(float x, float y) {
  376. if(x == 0) // special cases UP or DOWN
  377. return (y > 0) ? M_PI_2 : (y == 0) ? 0 : M_PI + M_PI_2;
  378. else if(y == 0) // special cases LEFT or RIGHT
  379. return (x >= 0) ? 0 : M_PI;
  380. float ret = atanf(y / x); // quadrant I
  381. if(x < 0 && y < 0) // quadrant III
  382. ret = (float)M_PI + ret;
  383. else if(x < 0) // quadrant II
  384. ret = (float)M_PI + ret; // it actually substracts
  385. else if(y < 0) // quadrant IV
  386. ret = (float)M_PI + (float)M_PI_2 + ((float)M_PI_2 + ret); // it actually substracts
  387. return ret;
  388. }
  389. // Matthias research - 10 minute physics
  390. bool Arc::collide(Ball& ball) {
  391. Vec2 dir = ball.p - p;
  392. float dist = dir.mag();
  393. // FURI_LOG_I(
  394. // TAG,
  395. // "ball.p: %.3f,%.3f p: %.3f,%.3f",
  396. // (double)ball.p.x,
  397. // (double)ball.p.y,
  398. // (double)p.x,
  399. // (double)p.y);
  400. // FURI_LOG_I(TAG, "dir: %.3f,%.3f dist: %.3f", (double)dir.x, (double)dir.y, (double)dist);
  401. if(surface == OUTSIDE) {
  402. if(dist > r + ball.r) {
  403. return false;
  404. }
  405. // FURI_LOG_I(TAG, "hitting arc");
  406. float angle = vector_to_angle(dir.x, -dir.y);
  407. if((start < end && start <= angle && angle <= end) ||
  408. (start > end && (angle >= start || angle <= end))) {
  409. // FURI_LOG_I(TAG, "colliding with arc");
  410. dir.normalize();
  411. Vec2 ball_v = ball.p - ball.prev_p;
  412. float corr = ball.r + r - dist;
  413. ball.p += dir * corr;
  414. float v = ball_v.dot(dir);
  415. ball_v += dir * (3.0f - v); // TODO: pushVel, this should be a prop
  416. ball.prev_p = ball.p - ball_v;
  417. return true;
  418. }
  419. }
  420. if(surface == INSIDE) {
  421. Vec2 prev_dir = ball.prev_p - p;
  422. float prev_dist = prev_dir.mag();
  423. if(prev_dist < r && dist + ball.r > r) {
  424. // FURI_LOG_I(TAG, "Inside an arc!");
  425. float angle = vector_to_angle(dir.x, -dir.y);
  426. // FURI_LOG_I(TAG, "%f : %f : %f", (double)start, (double)angle, (double)end);
  427. // if(angle >= start && angle <= end) {
  428. if((start < end && start <= angle && angle <= end) ||
  429. (start > end && (angle >= start || angle <= end))) {
  430. // FURI_LOG_I(TAG, "Within the arc angle");
  431. dir.normalize();
  432. Vec2 ball_v = ball.p - ball.prev_p;
  433. // correct our position to be "on" the arc
  434. float corr = dist + ball.r - r;
  435. ball.p -= dir * corr;
  436. // Adjust restitution on tangent and normals independently
  437. Vec2 tangent = {-dir.y, dir.x};
  438. float T = (ball_v.x * tangent.x + ball_v.y * tangent.y) * ARC_TANGENT_RESTITUTION;
  439. float N = (ball_v.x * tangent.y - ball_v.y * tangent.x) * ARC_NORMAL_RESTITUTION;
  440. ball_v.x = tangent.x * T - tangent.y * N;
  441. ball_v.y = tangent.y * T + tangent.x * N;
  442. // Current collision - works good, but handles restitution holistically
  443. // float v = ball_v.dot(dir);
  444. // ball_v -= dir * v * 2.0f * bounce;
  445. ball.prev_p = ball.p - ball_v;
  446. return true;
  447. }
  448. }
  449. }
  450. return false;
  451. }
  452. Bumper::Bumper(const Vec2& p_, float r_)
  453. : Arc(p_, r_) {
  454. score = 500;
  455. }
  456. void Bumper::draw(Canvas* canvas) {
  457. Arc::draw(canvas);
  458. if(decay) {
  459. // canvas_draw_disc(canvas, p.x / 10, p.y / 10, (r / 10) * 0.8f * (decay / 30.0f));
  460. gfx_draw_disc(canvas, p, r * 0.8f * (decay / 30.0f));
  461. }
  462. }
  463. void Bumper::reset_animation() {
  464. decay = 30;
  465. }
  466. void Bumper::step_animation() {
  467. if(decay > 20) {
  468. decay--;
  469. } else {
  470. decay = 0;
  471. }
  472. }
  473. void Rollover::draw(Canvas* canvas) {
  474. if(activated) {
  475. canvas_draw_str_aligned(canvas, p.x / 10, p.y / 10, AlignCenter, AlignCenter, c);
  476. } else {
  477. gfx_draw_dot(canvas, p);
  478. }
  479. }
  480. bool Rollover::collide(Ball& ball) {
  481. if(activated) {
  482. return false; // we've already rolled over it, prevent further signals
  483. }
  484. Vec2 dir = ball.p - p;
  485. float dist = dir.mag();
  486. if(dist < 30) {
  487. activated = true;
  488. return true;
  489. }
  490. return false;
  491. }
  492. // Reset the rollover
  493. void Rollover::signal_receive() {
  494. activated = false;
  495. }
  496. void Rollover::signal_send() {
  497. // maybe we should start a blink animation of the letters?
  498. }
  499. void Rollover::reset_state() {
  500. activated = false;
  501. }
  502. void Turbo::draw(Canvas* canvas) {
  503. gfx_draw_line(canvas, chevron_1[0], chevron_1[1]);
  504. gfx_draw_line(canvas, chevron_1[1], chevron_1[2]);
  505. gfx_draw_line(canvas, chevron_2[0], chevron_2[1]);
  506. gfx_draw_line(canvas, chevron_2[1], chevron_2[2]);
  507. }
  508. bool Turbo::collide(Ball& ball) {
  509. float dist = (ball.p - p).mag();
  510. // our distance check doesn't include the ball radius as we want the ball
  511. // to enter the turbo area a bit before being affected by the boost
  512. if(dist < r + 10) {
  513. // apply the turbo in 'dir' with force of 'boost'
  514. ball.prev_p = ball.p - (dir * (boost));
  515. }
  516. return false;
  517. }
  518. Plunger::Plunger(const Vec2& p_)
  519. : Object(p_, 20)
  520. , size(100) {
  521. compression = 0;
  522. }
  523. void Plunger::draw(Canvas* canvas) {
  524. // draw the end / striker
  525. canvas_draw_circle(canvas, p.x / 10, p.y / 10, r / 10);
  526. // draw a line, adjusted for compression
  527. // canvas_draw_line(
  528. // canvas,
  529. // roundf(p.x),
  530. // roundf(p.y),
  531. // roundf(p2.x),
  532. // //roundf(me->p.y - (plunger->size - plunger->compression))
  533. // roundf(p2.y));
  534. }
  535. void Chaser::draw(Canvas* canvas) {
  536. Vec2& p1 = points[0];
  537. Vec2& p2 = points[1];
  538. // TODO: feels like we can do all this with less code?
  539. switch(style) {
  540. case Style::SLASH: // / / / / / / / / /
  541. if(p1.x == p2.x) {
  542. int start = p1.y;
  543. int end = p2.y;
  544. if(start < end) {
  545. for(int y = start + offset; y < end; y += gap) {
  546. canvas_draw_line(canvas, p1.x - 2, y + 2, p1.x + 2, y - 2);
  547. }
  548. } else {
  549. for(int y = start - offset; y > end; y -= gap) {
  550. canvas_draw_line(canvas, p1.x - 2, y + 2, p1.x + 2, y - 2);
  551. }
  552. }
  553. } else if(p1.y == p2.y) {
  554. int start = p1.x;
  555. int end = p2.x;
  556. if(start < end) {
  557. for(int x = start + offset; x < end; x += gap) {
  558. canvas_draw_line(canvas, x - 2, p1.y + 2, x + 2, p1.y - 2);
  559. }
  560. } else {
  561. for(int x = start - offset; x > end; x -= gap) {
  562. canvas_draw_line(canvas, x - 2, p1.y + 2, x + 2, p1.y - 2);
  563. }
  564. }
  565. }
  566. break;
  567. default: // Style::SIMPLE, just dots
  568. // for all pixels between p and q, draw them with offset and gap
  569. if(p1.x == p2.x) {
  570. int start = p1.y;
  571. int end = p2.y;
  572. if(start < end) {
  573. for(int y = start + offset; y < end; y += gap) {
  574. canvas_draw_disc(canvas, p1.x, y, 1);
  575. }
  576. } else {
  577. for(int y = start - offset; y > end; y -= gap) {
  578. canvas_draw_disc(canvas, p1.x, y, 1);
  579. }
  580. }
  581. } else if(p1.y == p2.y) {
  582. int start = p1.x;
  583. int end = p2.x;
  584. if(start < end) {
  585. for(int x = start + offset; x < end; x += gap) {
  586. canvas_draw_disc(canvas, x, p1.y, 1);
  587. }
  588. } else {
  589. for(int x = start - offset; x > end; x -= gap) {
  590. canvas_draw_disc(canvas, x, p1.y, 1);
  591. }
  592. }
  593. }
  594. break;
  595. }
  596. }
  597. void Chaser::step_animation() {
  598. tick++;
  599. if(tick % (speed) == 0) {
  600. offset = (offset + 1) % gap;
  601. }
  602. }