objects.cxx 19 KB

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