title: Flutter-Flame: How to calculate the relect of a raycast?
body: <p>Thank you for your reading. My English is not good, I'll explain this as well as I can.
  I need to generate the reflection line in canvas, but it not work well.</p>
  <p><a href="https://i.stack.imgur.com/Qjd8d.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Qjd8d.jpg" alt="enter image description here" /></a></p>
  <p>I think my problem is the &quot;The formula for calculating the reflected vector&quot;.</p>
  <p>The mathematics I learned before is not the same as canvas, so I am very confused</p>
  <p>This is my code (wrong), you can run this (the first line is right, but the reflection is wrong):</p>
  <pre class="lang-dart prettyprint-override"><code>import 'package:flame/game.dart';
  import 'package:flame/input.dart';
  import 'package:flame_forge2d/body_component.dart';
  import 'package:flame_forge2d/flame_forge2d.dart';
  import 'package:flame_forge2d/forge2d_game.dart';
  import 'package:flutter/material.dart';
  
  import 'component/fixture/fixture_data.dart';
  
  void main() {
    runApp(
      GameWidget(
        game: TestGame(),
      ),
    );
  }
  
  class TestGame extends Forge2DGame with MultiTouchDragDetector {
    List&lt;Offset&gt; points = [Offset.zero, Offset.zero, Offset.zero];
    late var boundaries;
  
    @override
    Future&lt;void&gt; onLoad() async {
      await super.onLoad();
  
      boundaries = createBoundaries(this);
      boundaries.forEach(add);
  
      points[0] = (camera.canvasSize / 2).toOffset();
    }
  
    @override
    void update(double dt) {
      super.update(dt);
    }
  
    @override
    void render(Canvas canvas) {
      super.render(canvas);
  
      canvas.drawLine(points[0], points[1], Paint()..color = Colors.white);
  
      canvas.drawLine(points[1], points[2], Paint()..color = Colors.white);
  
      canvas.drawCircle(points[2], 30, Paint()..color = Colors.white);
    }
  
    /// @param [pointStart] start point of line
    /// @param [point2] second point of line
    /// @param [x] the position x of point need to calculate
    /// @return Offset of the point on line
    Offset calculateOffsetByX(Offset pointStart, Offset point2, double x) {
      //y =  ax + b
      final a = (pointStart.dy - point2.dy) / (pointStart.dx - point2.dx);
      final b = pointStart.dy - a * pointStart.dx;
      return Offset(x, a * x + b);
    }
  
    /// @param [pointStart] start point of line
    /// @param [point2] second point of line
    /// @param [y] the position y of point need to calculate
    /// @return Offset of the point on line
    Offset calculateOffsetByY(Offset pointStart, Offset point2, double y) {
      //y =  ax + b
      final a = (pointStart.dy - point2.dy) / (pointStart.dx - point2.dx);
      final b = pointStart.dy - a * pointStart.dx;
      return Offset((y - b) / a, y);
    }
  
    @override
    void onDragUpdate(int pointerId, DragUpdateInfo info) {
      var callback = MyRayCastCallBack(this);
  
      var finalPos = getFinalPos(screenToWorld(camera.viewport.effectiveSize) / 2,
          info.eventPosition.game);
  
      world.raycast(
          callback, screenToWorld(camera.viewport.effectiveSize) / 2, finalPos);
  
      var n = callback.normal;
      var i = info.eventPosition.game;
      var r = i + (n * (2 * i.dot(n)));
  
      var callback2 = MyRayCastCallBack2(this);
      world.raycast(callback2, callback.point, r);
    }
  
    Vector2 getFinalPos(Vector2 startPos, Vector2 touchPos) {
      return Vector2(
          calculateOffsetByY(startPos.toOffset(), touchPos.toOffset(), 0).dx,
          calculateOffsetByY(startPos.toOffset(), touchPos.toOffset(), 0).dy);
    }
  }
  
  class MyRayCastCallBack extends RayCastCallback {
    TestGame game;
    late Vector2 normal;
    late Vector2 point;
  
    MyRayCastCallBack(this.game);
  
    @override
    double reportFixture(
        Fixture fixture, Vector2 point, Vector2 normal, double fraction) {
      game.points[1] = game.worldToScreen(point).toOffset();
      this.normal = normal;
      this.point = point;
      return 0;
    }
  }
  
  class MyRayCastCallBack2 extends RayCastCallback {
    TestGame game;
    late Vector2 normal;
    late Vector2 point;
  
    MyRayCastCallBack2(this.game);
  
    @override
    double reportFixture(
        Fixture fixture, Vector2 point, Vector2 normal, double fraction) {
      game.points[2] = game.worldToScreen(point).toOffset();
      this.normal = normal;
      this.point = point;
      return 0;
    }
  }
  
  List&lt;Wall&gt; createBoundaries(Forge2DGame game) {
    /*final topLeft = Vector2.zero();
    final bottomRight = game.screenToWorld(game.camera.viewport.effectiveSize);
    final topRight = Vector2(bottomRight.x, topLeft.y);
    final bottomLeft = Vector2(topLeft.x, bottomRight.y);*/
    final bottomRight =
        game.screenToWorld(game.camera.viewport.effectiveSize) / 8 * 7;
  
    final topLeft = game.screenToWorld(game.camera.viewport.effectiveSize) / 8;
    final topRight = Vector2(bottomRight.x, topLeft.y);
    final bottomLeft = Vector2(topLeft.x, bottomRight.y);
  
    return [
      Wall(topLeft, topRight, FixtureKey.wallTop),
      Wall(topRight, bottomRight, FixtureKey.wallRight),
      Wall(bottomRight, bottomLeft, FixtureKey.wallBottom),
      Wall(bottomLeft, topLeft, FixtureKey.wallLeft),
    ];
  }
  
  class Wall extends BodyComponent {
    final Vector2 start;
    final Vector2 end;
    final FixtureKey fixtureKey;
  
    Wall(this.start, this.end, this.fixtureKey);
  
    @override
    Body createBody() {
      final shape = EdgeShape()..set(start, end);
  
      final fixtureDef = FixtureDef(shape)
        ..restitution = 0
        ..density = 1.0
        ..friction = 0
        ..userData = FixtureData(type: FixtureType.wall, key: fixtureKey);
      ;
  
      final bodyDef = BodyDef()
        ..userData = this // To be able to determine object in collision
        ..position = Vector2.zero()
        ..type = BodyType.static;
  
      return world.createBody(bodyDef)..createFixture(fixtureDef);
    }
  }
  
  
  </code></pre>