Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for dashed line #49

Open
tlvenn opened this issue Nov 16, 2022 · 10 comments
Open

Add support for dashed line #49

tlvenn opened this issue Nov 16, 2022 · 10 comments
Labels
enhancement New feature or request

Comments

@tlvenn
Copy link
Contributor

tlvenn commented Nov 16, 2022

Hi @roipeker,

Would there be an easy way to add support to draw dashed lines with Graphx ?
I remember having a similar ask when I was working with Graphic and the PR addressing it might give some pointers on how to implement it relatively easily.

entronad/graphic@33385a9

The gist of it being:
image
image

@roipeker
Copy link
Owner

roipeker commented Nov 16, 2022

Hi @tlvenn, I forgot to reply earlier today, sorry.

You can totally use that library.
Graphics class has getPaths() and drawPath() as input/output for raw Paths.

source:
graphx dash line

Output:
dash_example

Good luck!

@roipeker
Copy link
Owner

Feel free to reopen the issue if the solution doesn't fit your needs.

@tlvenn
Copy link
Contributor Author

tlvenn commented Nov 22, 2022

Hi @roipeker ,

Thanks a lot for the example, that is super useful !

The implementation for this within Graphics is simply a wrapper leveraging path_drawing lib. Could we do something similar so that we could draw dashed paths without importing an entire graph lib ? I might be able to contribute this given the example you provided.

Do you have a preference on how we should provide this within Graphx ?

@roipeker
Copy link
Owner

Probably as a lineStyle parameter?, although flash had no support for it i believe (api wise).

Anyway, depending on the level of complexity that u need, for straight dashed lines is much more performant to use shaders i think (gradients or image) than compute path segmentation.

@tlvenn
Copy link
Contributor Author

tlvenn commented Nov 25, 2022

Ya computing path segmentations seems pretty painful and expensive.. And yes while looking at it, I also discovered that Flash had no native support for this and people came with some pretty crazy stuff to do path segmentations on their own.

Right now my use cases is all around very simple straight lines so probably the path segmentation is relatively simple but would be curious to see how you would approach it using shaders if you can spare a little time.

Thanks in advance.

@roipeker
Copy link
Owner

Btw, by shaders i meant the classic Gradient or Image Shaders from flutter. Not the experimental GLSL (that will be probably removed with Impeller).

Give me a few mins and i will prepare a sample.

@tlvenn
Copy link
Contributor Author

tlvenn commented Nov 25, 2022

Thanks a lot @roipeker !

Speaking of which, what is the impact of Impeller on Graphx roughly ?

@roipeker
Copy link
Owner

roipeker commented Nov 25, 2022

No clue, never tried Impeller myself... but I mostly heard good things about it.

(This code uses the latest from master channel, generic Type for the addChild<T>) ;

Image shader example:

import 'package:graphx/graphx.dart';

import 'commons.dart';

class DashShaderScene extends GSprite {
  @override
  void addedToStage() {
    stage!.color = Colors.white;
    drawImageLine();
    super.addedToStage();
  }

  Future<void> drawImageLine() async {
    // create a temporal shape to draw as Image texture.
    var drawer = GShape()
      ..graphics
          .beginFill(Colors.redAccent)
          .drawRect(0, 0, 4, 2)
          .endFill()
          .beginFill(Colors.transparent)
          .drawRect(4, 0, 4, 2)
          .endFill();
    final dashTexture = await drawer.createImageTexture();

    var lines = addChild(GShape());
    lines.setPosition(100, 100);

    lines.graphics.lineStyle(2).lineBitmapStyle(dashTexture);
    lines.graphics.moveTo(0, 0).lineTo(100, 0);
    lines.graphics.endFill();
    // diagonal line
    var matrix = GMatrix();
    matrix.rotate(deg2rad(45));
    lines.graphics
        .lineStyle(4)
        .lineBitmapStyle(dashTexture, matrix)
        .moveTo(0, 0)
        .lineTo(100, 100)
        .endFill();

    // vertical line
    matrix.identity();
    matrix.rotate(deg2rad(90));

    lines.graphics
        .lineStyle(0)
        .lineBitmapStyle(dashTexture, matrix)
        .moveTo(0, 0)
        .lineTo(0, 80)
        .endFill();

    /// For other rotations, you should take the angle of the line.
    var from = GPoint(60, 30);
    var to = GPoint(180, 18);
    var angle = Math.atan2(to.y - from.y, to.x - from.x);

    matrix.identity();
    matrix.rotate(angle);
    lines.graphics
        .lineStyle(2)
        .lineBitmapStyle(dashTexture, matrix, true, true)
        .moveTo(from.x, from.y)
        .lineTo(to.x, to.y)
        .endFill();

    /// animation example
    var lines2 = addChild(GShape());
    lines2.setPosition(50, 20);
    matrix.identity();
    stage!.onEnterFrame.add((time) {
      matrix.tx += .8;
      lines2.graphics
          .clear()
          .lineStyle(4)
          .lineBitmapStyle(dashTexture, matrix, true, true)
          .lineTo(120, 0)
          .endFill();
    });
  }
}

Example video:

dashed_line_bitmap.mov

@roipeker
Copy link
Owner

roipeker commented Nov 25, 2022

Hey @tlvenn , here's the updated example code with shaders for Image and Gradient. Maybe not the prettiest solution as it is, but might be easy to get some "helper" class to simplify the usage. Anyway, is just another simplified option to the Path.

import 'package:graphx/graphx.dart';

import 'commons.dart';

class DashShaderScene extends GSprite {
  @override
  void addedToStage() {
    stage!.color = Colors.white;
    drawImageLine();
    drawGradientLine();
    super.addedToStage();
  }

  Future<void> drawGradientLine() async {
    var dash = addChild(GShape());
    dash.graphics.lineStyle(2);

    /// Gradient is "tricky" for rotations, there's no Matrix on the Flutter API side.
    /// unless we use Gradient.linear(), which lineGradientStyle() doesn't support.
    /// So the bounding box and the Alignment defines the gradient transformation.
    /// You have to workaround the math to figure the "dash" spacing and rotation.
    dash.graphics.lineGradientStyle(
      GradientType.linear,
      [Colors.blue, Colors.transparent],
      ratios: [0.5, 0.5],
      begin: const Alignment(0, 0),
      end: const Alignment(.2, 0),
      tileMode: TileMode.repeated,
    );
    dash.graphics.moveTo(0, 0).lineTo(100, 0).endFill();

    dash.graphics.lineStyle(2);
    dash.graphics.lineGradientStyle(
      GradientType.linear,
      [Colors.blue, Colors.transparent],
      ratios: [0.5, 0.5],
      begin: const Alignment(0, 0),
      end: const Alignment(0, .2),
      tileMode: TileMode.repeated,
    );
    dash.graphics.moveTo(0, 0).lineTo(0, 100).endFill();

    dash.setPosition(50, 130);
  }

  Future<void> drawImageLine() async {
    // create a temporal shape to draw as Image texture.
    var drawer = GShape()
      ..graphics
          .beginFill(Colors.redAccent)
          .drawRect(0, 0, 4, 2)
          .endFill()
          .beginFill(Colors.transparent)
          .drawRect(4, 0, 4, 2)
          .endFill();
    final dashTexture = await drawer.createImageTexture();

    var lines = addChild(GShape());
    lines.setPosition(30, 30);

    lines.graphics.lineStyle(2).lineBitmapStyle(dashTexture);
    lines.graphics.moveTo(0, 0).lineTo(100, 0);
    lines.graphics.endFill();
    // diagonal line
    var matrix = GMatrix();
    matrix.rotate(deg2rad(45));
    lines.graphics
        .lineStyle(4)
        .lineBitmapStyle(dashTexture, matrix)
        .moveTo(0, 0)
        .lineTo(50, 50)
        .endFill();

    // vertical line
    matrix.identity();
    matrix.rotate(deg2rad(90));

    lines.graphics
        .lineStyle(0)
        .lineBitmapStyle(dashTexture, matrix)
        .moveTo(0, 0)
        .lineTo(0, 80)
        .endFill();

    /// For other rotations, you should take the angle of the line.
    var from = GPoint(60, 30);
    var to = GPoint(180, 18);
    var angle = Math.atan2(to.y - from.y, to.x - from.x);

    matrix.identity();
    matrix.rotate(angle);
    lines.graphics
        .lineStyle(2)
        .lineBitmapStyle(dashTexture, matrix, true, true)
        .moveTo(from.x, from.y)
        .lineTo(to.x, to.y)
        .endFill();

    /// animation example
    var lines2 = addChild(GShape());
    lines2.setPosition(150, 10);
    matrix.identity();
    stage!.onEnterFrame.add((time) {
      matrix.tx += .8;
      lines2.graphics
          .clear()
          .lineStyle(4)
          .lineBitmapStyle(dashTexture, matrix, true, true)
          .lineTo(80, 0)
          .endFill();
    });
  }
}

Video (red lines = texture, blue lines = gradient)

dashed_line_shaders.mov

@roipeker
Copy link
Owner

Reopening the issue for future code references.

@roipeker roipeker reopened this Nov 25, 2022
@roipeker roipeker added the enhancement New feature or request label Nov 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants