1
1
package com .neueda .jetbrains .plugin .graphdb .visualization .renderers ;
2
2
3
- import com .neueda .jetbrains .plugin .graphdb .platform .ShouldNeverHappenException ;
4
- import com .neueda .jetbrains .plugin .graphdb .visualization .util .IntersectionUtil ;
5
3
import com .neueda .jetbrains .plugin .graphdb .visualization .util .RenderingUtil ;
6
4
import prefuse .Constants ;
7
5
import prefuse .render .EdgeRenderer ;
8
6
import prefuse .visual .EdgeItem ;
9
7
import prefuse .visual .VisualItem ;
8
+ import prefuse .visual .tuple .TableNodeItem ;
10
9
11
10
import java .awt .*;
12
11
import java .awt .geom .AffineTransform ;
13
12
import java .awt .geom .Point2D ;
14
13
import java .util .List ;
14
+ import java .util .Optional ;
15
+ import java .util .Spliterator ;
15
16
16
17
import static com .neueda .jetbrains .plugin .graphdb .visualization .constants .VisualizationParameters .EDGE_THICKNESS ;
17
18
import static com .neueda .jetbrains .plugin .graphdb .visualization .constants .VisualizationParameters .NODE_DIAMETER ;
19
+ import static java .lang .Math .*;
20
+ import static java .util .Spliterators .spliteratorUnknownSize ;
21
+ import static java .util .stream .Collectors .toList ;
22
+ import static java .util .stream .StreamSupport .stream ;
18
23
19
24
public class CustomEdgeRenderer extends EdgeRenderer {
20
25
21
26
private static final double RADIUS = (NODE_DIAMETER + EDGE_THICKNESS ) / 2 ;
27
+ private static final double RIGHT_ANGLE_IN_RADS = 1.5708 ;
22
28
23
29
public CustomEdgeRenderer (int edgeTypeLine ) {
24
30
super (edgeTypeLine );
@@ -30,59 +36,89 @@ protected Shape getRawShape(VisualItem item) {
30
36
VisualItem item1 = edge .getSourceItem ();
31
37
VisualItem item2 = edge .getTargetItem ();
32
38
33
- getAlignedPoint ( m_tmpPoints [ 0 ], item1 . getBounds (), m_xAlign1 , m_yAlign1 ) ;
34
- getAlignedPoint ( m_tmpPoints [ 1 ], item2 . getBounds (), m_xAlign2 , m_yAlign2 ) ;
35
- m_curWidth = ( float ) ( m_width * getLineWidth ( item ) );
39
+ boolean isDirected = m_edgeArrow != Constants . EDGE_ARROW_NONE ;
40
+ boolean isLoopNode = item1 == item2 ;
41
+ double shift = getRelationShift ( edge , item1 , item2 );
36
42
37
- // TODO decide on the angle here for loop arrow
38
- double angle = 0.261799 * 3 ;
43
+ double x1 = item1 .getBounds ().getCenterX ();
44
+ double y1 = item1 .getBounds ().getCenterY ();
45
+ double x2 = item2 .getBounds ().getCenterX ();
46
+ double y2 = item2 .getBounds ().getCenterY ();
39
47
40
- boolean isLoopNode = item1 == item2 ;
41
- if (!isLoopNode && edge .isDirected () && m_edgeArrow != Constants .EDGE_ARROW_NONE ) {
42
- boolean forward = (m_edgeArrow == Constants .EDGE_ARROW_FORWARD );
43
- Point2D start = m_tmpPoints [forward ? 0 : 1 ];
44
- Point2D end = m_tmpPoints [forward ? 1 : 0 ];
48
+ if (isLoopNode ) {
49
+ double angle = 0.261799 * 3 * shift ;
45
50
46
- VisualItem dest = forward ? edge . getTargetItem () : edge . getSourceItem ( );
47
- Point2D center = new Point2D . Double ( dest . getBounds (). getCenterX (), dest .getBounds (). getCenterY () );
48
- List < Point2D > intersections = IntersectionUtil . getCircleLineIntersectionPoint ( start , end , center , dest . getBounds (). getWidth () / 2 );
51
+ getAlignedPoint ( m_tmpPoints [ 0 ], item1 . getBounds (), m_xAlign1 , m_yAlign1 );
52
+ getAlignedPoint ( m_tmpPoints [ 1 ], item2 .getBounds (), m_xAlign2 , m_yAlign2 );
53
+ m_curWidth = ( float ) ( m_width * getLineWidth ( item ) );
49
54
50
- if (intersections .size () == 0 ) {
51
- throw new ShouldNeverHappenException ("Andrew Naydyonock" , "edge always intersect a node" );
55
+ if (isDirected ) {
56
+ double x = m_tmpPoints [0 ].getX ();
57
+ double y = m_tmpPoints [0 ].getY ();
58
+
59
+ Point .Double [] arrowPoints = RenderingUtil .arrow (angle , RADIUS , x , y , m_arrowHeight + 8 );
60
+ AffineTransform at = getArrowTrans (arrowPoints [0 ], arrowPoints [1 ], m_curWidth );
61
+ m_curArrow = at .createTransformedShape (m_arrowHead );
52
62
}
53
63
54
- end = intersections .get (0 );
64
+ return RenderingUtil .loopArrow (angle , RADIUS , x1 , y1 , m_arrowHeight );
65
+ } else {
66
+ double angle = RIGHT_ANGLE_IN_RADS - atan2 (y2 - y1 , x2 - x1 );
55
67
56
- AffineTransform at = getArrowTrans (start , end , m_curWidth );
57
- m_curArrow = at .createTransformedShape (m_arrowHead );
68
+ double shiftInRad = toRadians (shift * 20 );
58
69
59
- Point2D lineEnd = m_tmpPoints [forward ? 1 : 0 ];
60
- lineEnd .setLocation (0 , -m_arrowHeight );
61
- at .transform (lineEnd , lineEnd );
62
- } else if (isLoopNode && edge .isDirected () && m_edgeArrow != Constants .EDGE_ARROW_NONE ) {
63
- double x = m_tmpPoints [0 ].getX ();
64
- double y = m_tmpPoints [0 ].getY ();
70
+ double lineX1 = x1 + RADIUS * sin (angle + shiftInRad );
71
+ double lineY1 = y1 + RADIUS * cos (angle + shiftInRad );
65
72
66
- Point .Double [] arrowPoints = RenderingUtil .arrow (angle , RADIUS , x , y , m_arrowHeight + 8 );
67
- AffineTransform at = getArrowTrans (arrowPoints [0 ], arrowPoints [1 ], m_curWidth );
68
- m_curArrow = at .createTransformedShape (m_arrowHead );
69
- } else {
70
- m_curArrow = null ;
71
- }
73
+ double arrowX = x2 - RADIUS * sin (angle - shiftInRad );
74
+ double arrowY = y2 - RADIUS * cos (angle - shiftInRad );
72
75
73
- Shape shape ;
74
- double n1x = m_tmpPoints [0 ].getX ();
75
- double n1y = m_tmpPoints [0 ].getY ();
76
- double n2x = m_tmpPoints [1 ].getX ();
77
- double n2y = m_tmpPoints [1 ].getY ();
78
- if (isLoopNode ) {
79
- shape = RenderingUtil .loopArrow (angle , RADIUS , n1x , n1y , m_arrowHeight );
80
- } else {
81
- m_line .setLine (n1x , n1y , n2x , n2y );
82
- shape = m_line ;
76
+ if (isDirected ) {
77
+ AffineTransform at = getArrowTrans (new Point2D .Double (lineX1 , lineY1 ), new Point2D .Double (arrowX , arrowY ), m_curWidth );
78
+ m_curArrow = at .createTransformedShape (m_arrowHead );
79
+
80
+ double lineX2 = arrowX - m_arrowWidth * sin (angle );
81
+ double lineY2 = arrowY - m_arrowWidth * cos (angle );
82
+
83
+ m_line .setLine (lineX1 , lineY1 , lineX2 , lineY2 );
84
+ } else {
85
+ m_line .setLine (lineX1 , lineY1 , arrowX , arrowY );
86
+ }
87
+
88
+ return m_line ;
83
89
}
90
+ }
84
91
85
- return shape ;
92
+ private double getRelationShift (EdgeItem edge , VisualItem node1 , VisualItem node2 ) {
93
+ boolean reverse = node1 .getRow () > node2 .getRow ();
94
+ VisualItem node = reverse ? node1 : node2 ;
95
+
96
+ return cast (node , TableNodeItem .class )
97
+ .map (n -> {
98
+ Spliterator <?> iterator = spliteratorUnknownSize (n .edges (), 0 );
99
+ List <EdgeItem > edges = stream (iterator , false )
100
+ .map (e -> cast (e , EdgeItem .class ))
101
+ .filter (Optional ::isPresent )
102
+ .map (Optional ::get )
103
+ .filter (e ->
104
+ (e .getSourceItem () == node1 && e .getTargetItem () == node2 ) ||
105
+ (e .getSourceItem () == node2 && e .getTargetItem () == node1 )
106
+ )
107
+ .collect (toList ());
108
+
109
+ if (edges .size () > 1 ) {
110
+ int relNumber = edges .indexOf (edge );
111
+ double relPos = relNumber - edges .size () / 2 ;
112
+ if (edges .size () % 2 == 0 ) {
113
+ relPos = relPos + 0.5 ;
114
+ }
115
+
116
+ return reverse ? relPos : relPos * -1 ;
117
+ } else {
118
+ return 0d ;
119
+ }
120
+ })
121
+ .orElse (0d );
86
122
}
87
123
88
124
@ Override
@@ -97,4 +133,13 @@ public boolean locatePoint(Point2D p, VisualItem item) {
97
133
|| (m_curArrow != null && m_curArrow .contains (p .getX (), p .getY ()));
98
134
}
99
135
}
136
+
137
+ @ SuppressWarnings ("unchecked" )
138
+ private <T > Optional <T > cast (Object o , Class <T > clazz ) {
139
+ if (clazz .isInstance (o )) {
140
+ return Optional .of ((T ) o );
141
+ } else {
142
+ return Optional .empty ();
143
+ }
144
+ }
100
145
}
0 commit comments