1 /*******************************************************************************
2 * Copyright (c) 2020 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Semantum Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.connection.rendering;
14 import java.awt.geom.Arc2D;
15 import java.awt.geom.Path2D;
16 import java.awt.geom.PathIterator;
17 import java.awt.geom.Point2D;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.List;
22 public class ConnectionCrossings implements PathModifier {
30 private List<Segment> segments = new ArrayList<>();
34 public void setWidth(double gapWidth) {
35 this.width = gapWidth;
38 public double getWidth() {
42 public void setType(Type type) {
46 public Type getType() {
50 static class Segment {
51 public double x1, y1, x2, y2;
53 public Segment(double x1, double y1, double x2, double y2) {
65 static Double lineLineIntersection(Segment l1, Segment l2) {
66 double epsilon = 0.001;
68 double d = (l1.x1 - l1.x2) * (l2.y1 - l2.y2) - (l1.y1 - l1.y2) * (l2.x1 - l2.x2);
69 if (d == 0.0) return null;
70 double s = ((l1.x1 - l2.x1) * (l2.y1 - l2.y2) - (l1.y1 - l2.y1) * (l2.x1 - l2.x2)) / d;
71 if ((s > epsilon) && (s < 1 - epsilon)) {
72 double t = -((l1.x1 - l1.x2) * (l1.y1 - l2.y1) - (l1.y1 - l1.y2) * (l1.x1 - l2.x1)) / d;
73 if ((t > epsilon) && (t < 1 - epsilon)) {
80 public Path2D modify(Path2D path) {
81 Path2D.Double path2 = new Path2D.Double();
82 PathIterator iter = path.getPathIterator(null);
84 while (!iter.isDone()) {
86 double c[] = new double[6];
87 int i = iter.currentSegment(c);
89 case PathIterator.SEG_MOVETO:
90 path2.moveTo(c[0], c[1]);
92 case PathIterator.SEG_LINETO:
93 Segment l = new Segment(path2.getCurrentPoint().getX(), path2.getCurrentPoint().getY(), c[0], c[1]);
95 List<Double> gaps = new ArrayList<>();
96 for (Segment old : segments) {
97 Double t = lineLineIntersection(old, l);
103 if (gaps.isEmpty()) {
104 path2.lineTo(c[0], c[1]);
106 Collections.sort(gaps);
107 double dx = l.x2 - l.x1;
108 double dy = l.y2 - l.y1;
111 double len = Math.sqrt(dx*dx + dy*dy);
113 boolean finish = true;
114 Point2D prevGapEnd = null;
115 for (Double gapCenter : gaps) {
116 double pos2 = gapCenter - width / 2 / len;
117 double pos3 = gapCenter + width / 2 / len;
119 handleGap(path2, prevGapEnd);
121 path2.lineTo(l.x1 + pos2 * dx, l.y1 + pos2 * dy);
124 double x = l.x1 + pos3 * dx;
125 double y = l.y1 + pos3 * dy;
126 prevGapEnd = new Point2D.Double(x, y);
134 handleGap(path2, prevGapEnd);
135 path2.lineTo(l.x2, l.y2);
137 prevGapEnd = new Point2D.Double(l.x2, l.y2);
138 handleGap(path2, prevGapEnd);
144 case PathIterator.SEG_QUADTO:
145 // TODO: implement gaps
146 path2.quadTo(c[0], c[1], c[2], c[3]);
148 case PathIterator.SEG_CUBICTO:
149 // TODO: implement gaps
150 path2.curveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
152 case PathIterator.SEG_CLOSE:
153 // TODO: implement gaps
157 throw new RuntimeException("Unexpected segment type " + i);
164 private void handleGap(Path2D path, Point2D prevGapEnd) {
165 if (prevGapEnd != null) {
168 arcTo(path, prevGapEnd.getX(), prevGapEnd.getY());
171 squareTo(path, prevGapEnd.getX(), prevGapEnd.getY(), width);
174 path.moveTo(prevGapEnd.getX(), prevGapEnd.getY());
182 private static void arcTo(Path2D path, double x2, double y2) {
183 Arc2D arc = new Arc2D.Double();
184 double x1 = path.getCurrentPoint().getX();
185 double y1 = path.getCurrentPoint().getY();
188 double r = Math.sqrt(dx * dx + dy * dy) / 2;
189 double angle = Math.atan2(dx, dy) * 180 / Math.PI + 90;
190 double span = (angle > 225 || angle < 45) ? 180 : -180;
191 arc.setArcByCenter((x1 + x2) / 2, (y1 + y2) / 2, r, angle, span, Arc2D.OPEN);
192 path.append(arc, true);
195 private static void squareTo(Path2D path, double x2, double y2, double width) {
196 double x1 = path.getCurrentPoint().getX();
197 double y1 = path.getCurrentPoint().getY();
200 double l = Math.sqrt(dx * dx + dy* dy);
208 path.lineTo(x1 + nx * width / 2, y1 + ny * width / 2);
209 path.lineTo(x2 + nx * width / 2, y2 + ny * width / 2);