Arokip / flutter_diagram_editor

Fllutter diagram editor library
MIT License
116 stars 38 forks source link

Feature: Route lines across x OR y axis with 90 degree joints #33

Closed point-source closed 2 years ago

point-source commented 2 years ago

Is it possible to create a type of policy or feature which would force lines to be either vertical or horizontal and add 90 degree joints as necessary to reach a linked object? Similar to the default behavior of draw.io / lucidchart.

Something like this:

Screen Shot 2022-04-19 at 7 33 08 PM

Instead of this:

Screen Shot 2022-04-19 at 7 33 32 PM

Here are the draw.io pathfinding/line structure options for reference:

Screen Shot 2022-04-19 at 7 33 54 PM

I realize this could be accomplished by the user manually long-pressing the line to add a joint but the application I am building shouldn't require the user to manually route, if possible. How hard would this be to do?

Arokip commented 2 years ago

It would be really nice to have a policy inside the package that could be able to make these kind of connections. However, it's not an easy task to compute a path of the lines in all cases. And I can't think of an easy way how to add it to the package.

It's still possible to achieve the required behaviour. I recommend you to compute the position of the points after creating the connection between two components. And compute the same while moving any component.

Screenshot 2022-04-20 at 9 04 32

I created a code sample just for one of the cases (x-y-x). Please try to replace these functions in the example and play with it a little. Hopefully it will help you to make all the cases to work.

  @override
  onComponentScaleUpdate(componentId, details) {
    Offset positionDelta = details.localFocalPoint - lastFocalPoint;
    canvasWriter.model.moveComponent(componentId, positionDelta);
    final c1 = canvasReader.model.getComponent(componentId);
    for (final connection in c1.connections) {
      final c2 = canvasReader.model.getComponent(connection.otherComponentId);
      late final Offset p1;
      late final Offset p2;
      if (connection is ConnectionOut) {
        p1 = c1.position + c1.getPointOnComponent(Alignment.center);
        p2 = c2.position + c2.getPointOnComponent(Alignment.center);
      } else {
        p2 = c1.position + c1.getPointOnComponent(Alignment.center);
        p1 = c2.position + c2.getPointOnComponent(Alignment.center);
      }

      canvasWriter.model.setLinkMiddlePointPosition(connection.connectionId, Offset((p1.dx + p2.dx) / 2, p1.dy), 1);
      canvasWriter.model.setLinkMiddlePointPosition(connection.connectionId, Offset((p1.dx + p2.dx) / 2, p2.dy), 2);
    }
    canvasWriter.model.updateComponentLinks(componentId);
    lastFocalPoint = details.localFocalPoint;
  }
// This function tests if it's possible to connect the components and if yes, connects them
  bool connectComponents(String? sourceComponentId, String? targetComponentId) {
    if (sourceComponentId == null || targetComponentId == null) {
      return false;
    }
    // tests if the ids are not same (the same component)
    if (sourceComponentId == targetComponentId) {
      return false;
    }
    // tests if the connection between two components already exists (one way)
    if (canvasReader.model
        .getComponent(sourceComponentId)
        .connections
        .any((connection) => (connection is ConnectionOut) && (connection.otherComponentId == targetComponentId))) {
      return false;
    }

    // This connects two components (creates a link between), you can define the design of the link with LinkStyle.
    String linkId = canvasWriter.model.connectTwoComponents(
      sourceComponentId: sourceComponentId,
      targetComponentId: targetComponentId,
      linkStyle: LinkStyle(
        arrowType: ArrowType.pointedArrow,
        lineWidth: 1.5,
        backArrowType: ArrowType.centerCircle,
      ),
    );
    final c1 = canvasReader.model.getComponent(sourceComponentId);
    final c2 = canvasReader.model.getComponent(targetComponentId);
    final p1 = c1.position + c1.getPointOnComponent(Alignment.center);
    final p2 = c2.position + c2.getPointOnComponent(Alignment.center);

    canvasWriter.model.insertLinkMiddlePoint(linkId, Offset((p1.dx + p2.dx) / 2, p1.dy), 1);
    canvasWriter.model.insertLinkMiddlePoint(linkId, Offset((p1.dx + p2.dx) / 2, p2.dy), 2);

    canvasWriter.model.updateLink(linkId);

    return true;
  }
point-source commented 2 years ago

That actually turned out to be perfect for what I needed. Thank you for the really quick help with this!