objects.cxx 20 KB

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