LCOV - code coverage report
Current view: top level - dashql-app/src/ui/graph - graph_edges.ts Coverage Total Hit
Test: DashQL 915828b Lines: 99.4 % 491 488
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1            1 : export enum EdgeType {
       2              :     // Angle is multiple of 90
       3            1 :     West = 0,
       4            1 :     South = 1,
       5            1 :     East = 2,
       6            1 :     North = 3,
       7              :     // dy >= dx
       8            1 :     SouthWest = 4,
       9            1 :     SouthEast = 5,
      10            1 :     NorthEast = 6,
      11            1 :     NorthWest = 7,
      12              :     // dx > dy
      13            1 :     WestSouth = 8,
      14            1 :     EastSouth = 9,
      15            1 :     EastNorth = 10,
      16            1 :     WestNorth = 11,
      17              :     // dy >= dx && dx < width
      18            1 :     SouthWestSouth = 12,
      19            1 :     SouthEastSouth = 13,
      20            1 :     NorthEastNorth = 14,
      21            1 :     NorthWestNorth = 15,
      22              :     // dx > dy && dy < height
      23            1 :     WestSouthWest = 16,
      24            1 :     EastSouthEast = 17,
      25            1 :     EastNorthEast = 18,
      26            1 :     WestNorthWest = 19,
      27            1 : }
      28              : 
      29            1 : export enum NodePort {
      30            1 :     North = 0b0001,
      31            1 :     East = 0b0010,
      32            1 :     South = 0b0100,
      33            1 :     West = 0b1000,
      34            1 : }
      35              : 
      36            1 : export const PORTS_FROM = new Uint8Array([
      37            1 :     NodePort.West,
      38            1 :     NodePort.South,
      39            1 :     NodePort.East,
      40            1 :     NodePort.North,
      41            1 :     NodePort.South,
      42            1 :     NodePort.South,
      43            1 :     NodePort.North,
      44            1 :     NodePort.North,
      45            1 :     NodePort.West,
      46            1 :     NodePort.East,
      47            1 :     NodePort.East,
      48            1 :     NodePort.West,
      49            1 :     NodePort.South,
      50            1 :     NodePort.South,
      51            1 :     NodePort.North,
      52            1 :     NodePort.North,
      53            1 :     NodePort.West,
      54            1 :     NodePort.East,
      55            1 :     NodePort.East,
      56            1 :     NodePort.West,
      57            1 : ]);
      58              : 
      59            1 : export const PORTS_TO = new Uint8Array([
      60            1 :     NodePort.East,
      61            1 :     NodePort.North,
      62            1 :     NodePort.West,
      63            1 :     NodePort.South,
      64            1 :     NodePort.East,
      65            1 :     NodePort.West,
      66            1 :     NodePort.West,
      67            1 :     NodePort.East,
      68            1 :     NodePort.North,
      69            1 :     NodePort.North,
      70            1 :     NodePort.South,
      71            1 :     NodePort.South,
      72            1 :     NodePort.North,
      73            1 :     NodePort.North,
      74            1 :     NodePort.South,
      75            1 :     NodePort.South,
      76            1 :     NodePort.East,
      77            1 :     NodePort.West,
      78            1 :     NodePort.West,
      79            1 :     NodePort.East,
      80            1 : ]);
      81              : 
      82              : /// Atan2 circle https://commons.wikimedia.org/wiki/File:Atan2_circle.svg
      83            1 : export function selectEdgeTypeFromAngle(angle: number): EdgeType {
      84              :     // atan2 returns 180 for a target straight west, which is the sector -2 (West) as well
      85            1 :     const sector = angle === 180 ? -2 : angle / 90; // [-2, 2[
      86            1 :     if (sector == Math.floor(sector)) {
      87              :         // One of [-2, -1, 0, 1, 2]
      88              :         //
      89              :         // Becomes:
      90              :         //   West = 0,
      91              :         //   South = 1,
      92              :         //   East = 2,
      93              :         //   North = 3,
      94            1 :         return (sector + 2) as EdgeType; // [0, 4[
      95              :     } else {
      96              :         // We round the value, use the subsequent sector
      97              :         //
      98              :         // Becomes:
      99              :         //   SouthWest = 4,
     100              :         //   SouthEast = 5,
     101              :         //   NorthEast = 6,
     102              :         //   NorthWest = 7,
     103            1 :         return (Math.floor(sector) + 2 + 4) as EdgeType; // [4, 8[
     104              :     }
     105            1 : }
     106              : 
     107              : /// Select edge type using node dimensions
     108            1 : export function selectEdgeType(
     109            1 :     fromX: number,
     110            1 :     fromY: number,
     111            1 :     toX: number,
     112            1 :     toY: number,
     113            1 :     width: number,
     114            1 :     height: number,
     115            1 : ): EdgeType {
     116            1 :     const dx = toX - fromX;
     117            1 :     const dy = toY - fromY;
     118            1 :     const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
     119            1 :     let orientation = selectEdgeTypeFromAngle(angle);
     120              :     // const boxWidth = (widthX + widthY) / 2;
     121              :     // const boxHeight = (heightX + heightY) / 2;
     122            1 :     const boxDx = Math.max(Math.abs(dx), width) - width;
     123            1 :     const boxDy = Math.max(Math.abs(dy), height) - height;
     124            1 :     if (orientation >= 4) {
     125              :         // Is the horizontal distance larger than vertical?
     126              :         // Horizontal -> East/West first
     127              :         // Vertical   -> North/South first
     128            1 :         if (boxDx > boxDy) {
     129            1 :             orientation += 4; // [8, 12[
     130              : 
     131              :             // If dy < boxHeight, we cannot render like:
     132              :             //   |
     133              :             //   +--
     134              :             // but have to render like:
     135              :             //   --+
     136              :             //     |
     137              :             //     +--
     138              :             //
     139            1 :             if (Math.abs(dy) < height) {
     140            1 :                 orientation += 8; // [16, 20[
     141              :             }
     142            1 :         } else {
     143              :             // If dx < boxWidth, we cannot render like:
     144              :             //   --+
     145              :             //     |
     146              :             // but have to render like:
     147              :             //     |
     148              :             //     +-+
     149              :             //       |
     150              :             //
     151            1 :             if (Math.abs(dx) < width) {
     152            1 :                 orientation += 8; // [12, 16[
     153              :             }
     154              :         }
     155            1 :     }
     156            1 :     return orientation;
     157            1 : }
     158              : 
     159              : /// Select horizontal edge type using node dimensions
     160            1 : export function selectHorizontalEdgeType(
     161            1 :     fromX: number,
     162            1 :     fromY: number,
     163            1 :     toX: number,
     164            1 :     toY: number,
     165            1 : ): EdgeType {
     166            1 :     const dx = toX - fromX;
     167            1 :     const dy = toY - fromY;
     168            1 :     const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
     169            1 :     let orientation = selectEdgeTypeFromAngle(angle);
     170            1 :     if (orientation >= 4) {
     171            1 :         return orientation + 12; // [16, 20[
     172              :     } else {
     173            1 :         return orientation;
     174              :     }
     175            1 : }
     176              : 
     177              : /// Select vertical edge type using node dimensions
     178            1 : export function selectVerticalEdgeType(
     179            1 :     fromX: number,
     180            1 :     fromY: number,
     181            1 :     toX: number,
     182            1 :     toY: number,
     183            1 : ): EdgeType {
     184            1 :     const dx = toX - fromX;
     185            1 :     const dy = toY - fromY;
     186            1 :     const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
     187            1 :     let orientation = selectEdgeTypeFromAngle(angle);
     188            1 :     if (orientation >= 4) {
     189            1 :         return orientation + 8; // [12, 16[
     190              :     } else {
     191            1 :         return orientation;
     192              :     }
     193            1 : }
     194              : 
     195            1 : export enum PathType {
     196            1 :     NONE,
     197            1 :     DIRECT,
     198            1 :     SINGLE_TURN,
     199            1 :     TWO_TURNS,
     200            1 : }
     201              : 
     202            1 : export class PathBuilder {
     203              :     /// The path type
     204            1 :     pathType: PathType;
     205              :     /// The path buffer
     206            1 :     path: Float64Array;
     207              :     /// The current size
     208            1 :     i: number;
     209              : 
     210            1 :     constructor() {
     211            1 :         this.pathType = PathType.NONE;
     212            1 :         this.path = new Float64Array(16);
     213            1 :         this.i = 0;
     214            1 :     }
     215            1 :     clear() {
     216            1 :         for (let i = 0; i < 16; ++i) {
     217            1 :             this.path[i] = 0;
     218              :         }
     219            1 :         this.i = 0;
     220            1 :     }
     221            0 :     last(): [number, number] {
     222            0 :         return (this.i < 2) ? [0, 0] : [this.path[this.i - 2], this.path[this.i - 1]];
     223            0 :     }
     224            1 :     begin(x: number, y: number) {
     225            1 :         this.clear();
     226            1 :         this.path[0] = x;
     227            1 :         this.path[1] = y;
     228            1 :     }
     229            1 :     push(x: number, y: number) {
     230            1 :         this.i += 2;
     231            1 :         this.path[this.i] = x;
     232            1 :         this.path[this.i + 1] = y;
     233            1 :     }
     234            1 :     finish(pathType: PathType): PathBuilder {
     235            1 :         this.pathType = pathType;
     236            1 :         return this;
     237            1 :     }
     238            1 :     render(standalone: boolean = true): string {
     239            1 :         const p = this.path;
     240            1 :         const prefix = standalone ? 'M' : 'L';
     241            1 :         switch (this.pathType) {
     242            1 :             case PathType.NONE:
     243            1 :                 return "";
     244            1 :             case PathType.DIRECT:
     245            1 :                 return `${prefix} ${p[0]} ${p[1]} L ${p[2]} ${p[3]}`;
     246            1 :             case PathType.SINGLE_TURN:
     247            1 :                 return `${prefix} ${p[0]} ${p[1]} L ${p[2]} ${p[3]} Q ${p[4]} ${p[5]}, ${p[6]} ${p[7]} L ${p[8]} ${p[9]}`;
     248            1 :             case PathType.TWO_TURNS:
     249            1 :                 return `${prefix} ${p[0]} ${p[1]} L ${p[2]} ${p[3]} Q ${p[4]} ${p[5]}, ${p[6]} ${p[7]} L ${p[8]} ${p[9]} Q ${p[10]} ${p[11]}, ${p[12]} ${p[13]} L ${p[14]} ${p[15]}`;
     250            1 :         }
     251            1 :     }
     252            1 : }
     253              : 
     254            1 : export function buildEdgePathBetweenRectangles(
     255            1 :     builder: PathBuilder,
     256            1 :     type: EdgeType,
     257            1 :     fromCenterX: number,
     258            1 :     fromCenterY: number,
     259            1 :     toCenterX: number,
     260            1 :     toCenterY: number,
     261            1 :     fromWidth: number,
     262            1 :     fromHeight: number,
     263            1 :     toWidth: number,
     264            1 :     toHeight: number,
     265            1 :     cornerRadius: number,
     266            1 :     offset: number = 0,
     267            1 : ): PathBuilder {
     268            1 :     if (toCenterX - fromCenterX == 0 && toCenterY - fromCenterY == 0) {
     269            1 :         return builder;
     270              :     }
     271            1 :     const r = cornerRadius;
     272              : 
     273              :     // Note that North/South/East/West is a bit confusing here in the context of SVG:
     274              :     //  - We use North as North relative to the target coordinate system.
     275              :     //  - That means North == Larger Y, South == Smaller Y
     276              :     //  - This breaks intuition in SVG since there (0, 0) is in the top-left!
     277              :     //  - In SVG, don't assume that NORTH means an edge is going upwards!
     278              : 
     279              :     // The offset is applied in counter clockwise direction.
     280              : 
     281            1 :     switch (type) {
     282              :         // DIRECT
     283              : 
     284              :         //  B
     285              :         //  |
     286              :         //  A
     287            1 :         case EdgeType.North:
     288            1 :             fromCenterX += offset;
     289            1 :             toCenterX += offset;
     290            1 :             builder.begin(fromCenterX, fromCenterY + fromHeight / 2);
     291            1 :             builder.push(toCenterX, toCenterY - toHeight / 2);
     292            1 :             return builder.finish(PathType.DIRECT);
     293              : 
     294              :         //  A
     295              :         //  |
     296              :         //  B
     297            1 :         case EdgeType.South:
     298            1 :             fromCenterX -= offset;
     299            1 :             toCenterX -= offset;
     300            1 :             builder.begin(fromCenterX, fromCenterY - fromHeight / 2);
     301            1 :             builder.push(toCenterX, toCenterY + toHeight / 2);
     302            1 :             return builder.finish(PathType.DIRECT);
     303              : 
     304              :         //  A-B
     305            1 :         case EdgeType.East:
     306            1 :             fromCenterY -= offset;
     307            1 :             toCenterY -= offset;
     308            1 :             builder.begin(fromCenterX + fromWidth / 2, fromCenterY);
     309            1 :             builder.push(toCenterX - toWidth / 2, toCenterY);
     310            1 :             return builder.finish(PathType.DIRECT);
     311              : 
     312              :         //  B-A
     313            1 :         case EdgeType.West:
     314            1 :             fromCenterY += offset;
     315            1 :             toCenterY += offset;
     316            1 :             builder.begin(fromCenterX - fromWidth / 2, fromCenterY);
     317            1 :             builder.push(toCenterX + toWidth / 2, toCenterY);
     318            1 :             return builder.finish(PathType.DIRECT);
     319              : 
     320              :         // 1 TURN
     321              : 
     322              :         //  +-B
     323              :         //  |
     324              :         //  A
     325            1 :         case EdgeType.NorthEast: {
     326            1 :             fromCenterY += fromHeight / 2;
     327            1 :             toCenterX -= toWidth / 2;
     328            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     329            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     330            1 :             fromCenterX += offset;
     331            1 :             toCenterY -= offset;
     332            1 :             builder.begin(fromCenterX, fromCenterY);
     333            1 :             builder.push(fromCenterX, toCenterY - Math.min(diffY / 2, r) - r);
     334            1 :             builder.push(fromCenterX, toCenterY);
     335            1 :             builder.push(fromCenterX + Math.min(diffX / 2, r), toCenterY);
     336            1 :             builder.push(toCenterX, toCenterY);
     337            1 :             return builder.finish(PathType.SINGLE_TURN);
     338            1 :         }
     339              : 
     340              :         //  B-+
     341              :         //    |
     342              :         //    A
     343            1 :         case EdgeType.NorthWest: {
     344            1 :             fromCenterY += fromHeight / 2;
     345            1 :             toCenterX += toWidth / 2;
     346            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     347            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     348            1 :             fromCenterX += offset;
     349            1 :             toCenterY += offset;
     350            1 :             builder.begin(fromCenterX, fromCenterY);
     351            1 :             builder.push(fromCenterX, toCenterY - Math.min(diffY / 2, r));
     352            1 :             builder.push(fromCenterX, toCenterY);
     353            1 :             builder.push(fromCenterX - Math.min(diffX / 2, r), toCenterY);
     354            1 :             builder.push(toCenterX, toCenterY);
     355            1 :             return builder.finish(PathType.SINGLE_TURN);
     356            1 :         }
     357              : 
     358              :         //  A
     359              :         //  |
     360              :         //  +-B
     361            1 :         case EdgeType.SouthEast: {
     362            1 :             fromCenterY -= fromHeight / 2;
     363            1 :             toCenterX -= toWidth / 2;
     364            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     365            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     366            1 :             fromCenterX -= offset;
     367            1 :             toCenterY -= offset;
     368            1 :             builder.begin(fromCenterX, fromCenterY);
     369            1 :             builder.push(fromCenterX, toCenterY + Math.min(diffY / 2, r));
     370            1 :             builder.push(fromCenterX, toCenterY);
     371            1 :             builder.push(fromCenterX + Math.min(diffX / 2, r), toCenterY);
     372            1 :             builder.push(toCenterX, toCenterY);
     373            1 :             return builder.finish(PathType.SINGLE_TURN);
     374            1 :         }
     375              : 
     376              :         //    A
     377              :         //    |
     378              :         //  B-+
     379            1 :         case EdgeType.SouthWest: {
     380            1 :             fromCenterY -= fromHeight / 2;
     381            1 :             toCenterX += toWidth / 2;
     382            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     383            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     384            1 :             fromCenterX -= offset;
     385            1 :             toCenterY += offset;
     386            1 :             builder.begin(fromCenterX, fromCenterY);
     387            1 :             builder.push(fromCenterX, toCenterY + Math.min(diffY / 2, r));
     388            1 :             builder.push(fromCenterX, toCenterY);
     389            1 :             builder.push(fromCenterX - Math.min(diffX / 2, r), toCenterY);
     390            1 :             builder.push(toCenterX, toCenterY);
     391            1 :             return builder.finish(PathType.SINGLE_TURN);
     392            1 :         }
     393              : 
     394              :         //    B
     395              :         //    |
     396              :         //  A-+
     397            1 :         case EdgeType.EastNorth: {
     398            1 :             fromCenterX += fromWidth / 2;
     399            1 :             toCenterY -= toHeight / 2;
     400            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     401            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     402            1 :             fromCenterY -= offset;
     403            1 :             toCenterX += offset;
     404            1 :             builder.begin(fromCenterX, fromCenterY);
     405            1 :             builder.push(toCenterX - Math.min(diffX / 2, r), fromCenterY);
     406            1 :             builder.push(toCenterX, fromCenterY);
     407            1 :             builder.push(toCenterX, fromCenterY + Math.min(diffY / 2, r));
     408            1 :             builder.push(toCenterX, toCenterY);
     409            1 :             return builder.finish(PathType.SINGLE_TURN);
     410            1 :         }
     411              : 
     412              :         //  A-+
     413              :         //    |
     414              :         //    B
     415            1 :         case EdgeType.EastSouth: {
     416            1 :             fromCenterX += fromWidth / 2;
     417            1 :             toCenterY += toHeight / 2;
     418            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     419            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     420            1 :             fromCenterY -= offset;
     421            1 :             toCenterX -= offset;
     422            1 :             builder.begin(fromCenterX, fromCenterY);
     423            1 :             builder.push(toCenterX - Math.min(diffX / 2, r), fromCenterY);
     424            1 :             builder.push(toCenterX, fromCenterY);
     425            1 :             builder.push(toCenterX, fromCenterY - Math.min(diffY / 2, r));
     426            1 :             builder.push(toCenterX, toCenterY);
     427            1 :             return builder.finish(PathType.SINGLE_TURN);
     428            1 :         }
     429              : 
     430              :         //  B
     431              :         //  |
     432              :         //  +-A
     433            1 :         case EdgeType.WestNorth: {
     434            1 :             fromCenterX -= fromWidth / 2;
     435            1 :             toCenterY -= toHeight / 2;
     436            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     437            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     438            1 :             fromCenterY += offset;
     439            1 :             toCenterX += offset;
     440            1 :             builder.begin(fromCenterX, fromCenterY);
     441            1 :             builder.push(toCenterX + Math.min(diffX / 2, r), fromCenterY);
     442            1 :             builder.push(toCenterX, fromCenterY);
     443            1 :             builder.push(toCenterX, fromCenterY + Math.min(diffY / 2, r));
     444            1 :             builder.push(toCenterX, toCenterY);
     445            1 :             return builder.finish(PathType.SINGLE_TURN);
     446            1 :         }
     447              : 
     448              :         //  +-A
     449              :         //  |
     450              :         //  B
     451            1 :         case EdgeType.WestSouth: {
     452            1 :             fromCenterX -= fromWidth / 2;
     453            1 :             toCenterY += toHeight / 2;
     454            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     455            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     456            1 :             fromCenterY -= offset;
     457            1 :             toCenterX += offset;
     458            1 :             builder.begin(fromCenterX, fromCenterY);
     459            1 :             builder.push(toCenterX + Math.min(diffX / 2, r), fromCenterY);
     460            1 :             builder.push(toCenterX, fromCenterY);
     461            1 :             builder.push(toCenterX, fromCenterY - Math.min(diffY / 2, r));
     462            1 :             builder.push(toCenterX, toCenterY);
     463            1 :             return builder.finish(PathType.SINGLE_TURN);
     464            1 :         }
     465              : 
     466              :         // 2 TURNS
     467              : 
     468              :         //   +-B
     469              :         //   |
     470              :         // A-+
     471            1 :         case EdgeType.EastNorthEast: {
     472            1 :             fromCenterX += fromWidth / 2;
     473            1 :             toCenterX -= toWidth / 2;
     474            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     475            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     476            1 :             let midX = fromCenterX + diffX / 2;
     477            1 :             fromCenterY -= offset;
     478            1 :             toCenterY -= offset;
     479            1 :             midX += offset;
     480            1 :             builder.begin(fromCenterX, fromCenterY);
     481            1 :             builder.push(midX - Math.min(diffX / 2, r), fromCenterY);
     482            1 :             builder.push(midX, fromCenterY);
     483            1 :             builder.push(midX, fromCenterY + Math.min(diffY / 2, r));
     484            1 :             builder.push(midX, toCenterY - Math.min(diffY / 2, r));
     485            1 :             builder.push(midX, toCenterY);
     486            1 :             builder.push(midX + Math.min(diffX / 2, r), toCenterY);
     487            1 :             builder.push(toCenterX, toCenterY);
     488            1 :             return builder.finish(PathType.TWO_TURNS);
     489            1 :         }
     490              : 
     491              :         // A-+
     492              :         //   |
     493              :         //   +-B
     494            1 :         case EdgeType.EastSouthEast: {
     495            1 :             fromCenterX += fromWidth / 2;
     496            1 :             toCenterX -= toWidth / 2;
     497            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     498            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     499            1 :             let midX = fromCenterX + diffX / 2;
     500            1 :             fromCenterY -= offset;
     501            1 :             toCenterY -= offset;
     502            1 :             midX -= offset;
     503            1 :             builder.begin(fromCenterX, fromCenterY);
     504            1 :             builder.push(midX - Math.min(diffX / 2, r), fromCenterY);
     505            1 :             builder.push(midX, fromCenterY);
     506            1 :             builder.push(midX, fromCenterY - Math.min(diffY / 2, r));
     507            1 :             builder.push(midX, toCenterY + Math.min(diffY / 2, r));
     508            1 :             builder.push(midX, toCenterY);
     509            1 :             builder.push(midX + Math.min(diffX / 2, r), toCenterY);
     510            1 :             builder.push(toCenterX, toCenterY);
     511            1 :             return builder.finish(PathType.TWO_TURNS);
     512            1 :         }
     513              : 
     514              :         // A
     515              :         // |
     516              :         // +-+
     517              :         //   |
     518              :         //   B
     519            1 :         case EdgeType.SouthEastSouth: {
     520            1 :             fromCenterY -= fromHeight / 2;
     521            1 :             toCenterY += toHeight / 2;
     522            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     523            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     524            1 :             let midY = fromCenterY - diffY / 2;
     525            1 :             fromCenterX -= offset;
     526            1 :             toCenterX -= offset;
     527            1 :             midY -= offset;
     528            1 :             builder.begin(fromCenterX, fromCenterY);
     529            1 :             builder.push(fromCenterX, midY + Math.min(diffY / 2, r));
     530            1 :             builder.push(fromCenterX, midY);
     531            1 :             builder.push(fromCenterX + Math.min(diffX / 2, r), midY);
     532            1 :             builder.push(toCenterX - Math.min(diffX / 2, r), midY);
     533            1 :             builder.push(toCenterX, midY);
     534            1 :             builder.push(toCenterX, midY - Math.min(diffY / 2, r));
     535            1 :             builder.push(toCenterX, toCenterY);
     536            1 :             return builder.finish(PathType.TWO_TURNS);
     537            1 :         }
     538              : 
     539              :         //   A
     540              :         //   |
     541              :         // +-+
     542              :         // |
     543              :         // B
     544            1 :         case EdgeType.SouthWestSouth: {
     545            1 :             fromCenterY -= fromHeight / 2;
     546            1 :             toCenterY += toHeight / 2;
     547            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     548            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     549            1 :             let midY = fromCenterY - diffY / 2;
     550            1 :             fromCenterX -= offset;
     551            1 :             toCenterX -= offset
     552            1 :             midY += offset;
     553            1 :             builder.begin(fromCenterX, fromCenterY);
     554            1 :             builder.push(fromCenterX, midY + Math.min(diffY / 2, r));
     555            1 :             builder.push(fromCenterX, midY);
     556            1 :             builder.push(fromCenterX - Math.min(diffX / 2, r), midY);
     557            1 :             builder.push(toCenterX + Math.min(diffX / 2, r), midY);
     558            1 :             builder.push(toCenterX, midY);
     559            1 :             builder.push(toCenterX, midY - Math.min(diffY / 2, r));
     560            1 :             builder.push(toCenterX, toCenterY);
     561            1 :             return builder.finish(PathType.TWO_TURNS);
     562            1 :         }
     563              : 
     564              :         // B-+
     565              :         //   |
     566              :         //   +-A
     567            1 :         case EdgeType.WestNorthWest: {
     568            1 :             fromCenterX -= fromWidth / 2;
     569            1 :             toCenterX += toWidth / 2;
     570            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     571            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     572            1 :             let midX = fromCenterX - diffX / 2;
     573            1 :             fromCenterY += offset;
     574            1 :             toCenterY += offset;
     575            1 :             midX += offset;
     576            1 :             builder.begin(fromCenterX, fromCenterY);
     577            1 :             builder.push(midX + Math.min(diffX / 2, r), fromCenterY);
     578            1 :             builder.push(midX, fromCenterY);
     579            1 :             builder.push(midX, fromCenterY + Math.min(diffY / 2, r));
     580            1 :             builder.push(midX, toCenterY - Math.min(diffY / 2, r));
     581            1 :             builder.push(midX, toCenterY);
     582            1 :             builder.push(midX - Math.min(diffX / 2, r), toCenterY);
     583            1 :             builder.push(toCenterX, toCenterY);
     584            1 :             return builder.finish(PathType.TWO_TURNS);
     585            1 :         }
     586              : 
     587              :         //   +-A
     588              :         //   |
     589              :         // B-+
     590            1 :         case EdgeType.WestSouthWest: {
     591            1 :             fromCenterX -= fromWidth / 2;
     592            1 :             toCenterX += toWidth / 2;
     593            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     594            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     595            1 :             let midX = fromCenterX - diffX / 2;
     596            1 :             fromCenterY += offset;
     597            1 :             toCenterY += offset;
     598            1 :             midX -= offset;
     599            1 :             builder.begin(fromCenterX, fromCenterY);
     600            1 :             builder.push(midX + Math.min(diffX / 2, r), fromCenterY);
     601            1 :             builder.push(midX, fromCenterY);
     602            1 :             builder.push(midX, fromCenterY - Math.min(diffY / 2, r));
     603            1 :             builder.push(midX, toCenterY + Math.min(diffY / 2, r));
     604            1 :             builder.push(midX, toCenterY);
     605            1 :             builder.push(midX - Math.min(diffX / 2, r), toCenterY);
     606            1 :             builder.push(toCenterX, toCenterY);
     607            1 :             return builder.finish(PathType.TWO_TURNS);
     608            1 :         }
     609              : 
     610              :         //   B
     611              :         //   |
     612              :         // +-+
     613              :         // |
     614              :         // A
     615            1 :         case EdgeType.NorthEastNorth: {
     616            1 :             fromCenterY += fromHeight / 2;
     617            1 :             toCenterY -= toHeight / 2;
     618            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     619            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     620            1 :             let midY = fromCenterY + diffY / 2;
     621            1 :             fromCenterX += offset;
     622            1 :             toCenterX += offset;
     623            1 :             midY -= offset;
     624            1 :             builder.begin(fromCenterX, fromCenterY);
     625            1 :             builder.push(fromCenterX, midY - Math.min(diffY / 2, r));
     626            1 :             builder.push(fromCenterX, midY);
     627            1 :             builder.push(fromCenterX + Math.min(diffX / 2, r), midY);
     628            1 :             builder.push(toCenterX - Math.min(diffX / 2, r), midY);
     629            1 :             builder.push(toCenterX, midY);
     630            1 :             builder.push(toCenterX, midY + Math.min(diffY / 2, r));
     631            1 :             builder.push(toCenterX, toCenterY);
     632            1 :             return builder.finish(PathType.TWO_TURNS);
     633            1 :         }
     634              : 
     635              :         // B
     636              :         // |
     637              :         // +-+
     638              :         //   |
     639              :         //   A
     640            1 :         case EdgeType.NorthWestNorth: {
     641            1 :             fromCenterY += fromHeight / 2;
     642            1 :             toCenterY -= toHeight / 2;
     643            1 :             const diffX = Math.abs(toCenterX - fromCenterX);
     644            1 :             const diffY = Math.abs(toCenterY - fromCenterY);
     645            1 :             let midY = fromCenterY + diffY / 2;
     646            1 :             fromCenterX += offset;
     647            1 :             toCenterX += offset;
     648            1 :             midY += offset;
     649            1 :             builder.begin(fromCenterX, fromCenterY);
     650            1 :             builder.push(fromCenterX, midY - Math.min(diffY / 2, r));
     651            1 :             builder.push(fromCenterX, midY);
     652            1 :             builder.push(fromCenterX - Math.min(diffX / 2, r), midY);
     653            1 :             builder.push(toCenterX + Math.min(diffX / 2, r), midY);
     654            1 :             builder.push(toCenterX, midY);
     655            1 :             builder.push(toCenterX, midY + Math.min(diffY / 2, r));
     656            1 :             builder.push(toCenterX, toCenterY);
     657            1 :             return builder.finish(PathType.TWO_TURNS);
     658            1 :         }
     659            1 :     }
     660            1 : }
        

Generated by: LCOV version 2.0-1