LCOV - code coverage report
Current view: top level - pencil/include/layout - layout_quality.inl (source / functions) Coverage Total Hit
Test: crystal-facet-uml_v1.71.2_covts Lines: 0.0 % 241 0
Test Date: 2026-08-22 19:47:36 Functions: 0.0 % 12 0

            Line data    Source code
       1              : /* File: layout_quality.inl; Copyright and License: see below */
       2              : 
       3              : #include "u8/u8_trace.h"
       4              : #include <assert.h>
       5              : 
       6            0 : static inline void layout_quality_init ( layout_quality_t *this_, const pencil_size_t *pencil_size )
       7              : {
       8            0 :     (*this_).pencil_size = pencil_size;
       9            0 : }
      10              : 
      11            0 : static inline layout_quality_t layout_quality_new ( const pencil_size_t *pencil_size )
      12              : {
      13              :     layout_quality_t result;
      14            0 :     layout_quality_init( &result, pencil_size );
      15            0 :     return result;
      16              : }
      17              : 
      18              : static inline void layout_quality_destroy ( layout_quality_t *this_ )
      19              : {
      20              : }
      21              : 
      22              : /* NO-GOs */
      23              : 
      24              : /* if an object is forbidden (e.g. german swastika): */
      25              : #define LAYOUT_QUALITY_WEIGHT_FORBIDDEN (1000000.0)
      26              : /* if an object is not fully contained in the diagrams drawing area: */
      27              : #define LAYOUT_QUALITY_WEIGHT_NOT_IN_DIAGRAM_AREA (1000.0)
      28              : 
      29              : /* OVERLAPS */
      30              : 
      31              : /* if an objects label or type-icon crosses another objects label or type-icon: */
      32              : #define LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP (100.0)
      33              : /* if an objects label or type-icon crosses another objects contour or connection line: */
      34              : #define LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE (10.0)
      35              : /* if an objects contour or connection line is shared with another objects contour or connection line: */
      36              : #define LAYOUT_QUALITY_WEIGHT_SHARED_LINES (10.0)
      37              : /* if an objects contour or connection line crosses another objects contour or connection line: */
      38              : #define LAYOUT_QUALITY_WEIGHT_CROSS_LINES (10.0)
      39              : /* if an objects contour or connection line crosses another objects envelope-area: */
      40              : #define LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA (5.0)
      41              : /* if an objects envelope-area crosses another objects envelope-area: */
      42              : #define LAYOUT_QUALITY_WEIGHT_CROSS_AREAS (1.0)
      43              : 
      44              : /* SUBOPTIMAL LOCATIONS OR DISTANCES */
      45              : 
      46              : /* if an object shall be avoided to find nice solutions and not run into a local layouting optimum that is forbidden: */
      47              : #define LAYOUT_QUALITY_WEIGHT_AVOID (10.0)
      48              : /* if a location is not nice (too short line segment, too far from source or target): */
      49              : #define LAYOUT_QUALITY_WEIGHT_LOCATION (3.0)
      50              : /* if an objects contour, label or type-icon is too far from the target location or a connection line is longer than needed: */
      51              : #define LAYOUT_QUALITY_WEIGHT_DISTANCE (1.0)
      52              : 
      53            0 : static inline double layout_quality_debts_class_diag( const layout_quality_t *this_,
      54              :                                                       const layout_visible_classifier_t *probe,
      55              :                                                       const geometry_offset_t *order_target,
      56              :                                                       const layout_diagram_t *other )
      57              : {
      58            0 :     assert( probe != NULL );
      59            0 :     assert( order_target != NULL );
      60            0 :     assert( other != NULL );
      61            0 :     double debts = 0.0;
      62              : 
      63              :     const geometry_rectangle_t *const diagram_draw_area
      64            0 :         = layout_diagram_get_draw_area_const( other );
      65              : 
      66              :     const geometry_rectangle_t *const classifier_bounds
      67            0 :         = layout_visible_classifier_get_envelope_box_const( probe );
      68              : 
      69              :     /* add debts for overlap to diagram boundary */
      70            0 :     if ( ! geometry_rectangle_is_containing( diagram_draw_area, classifier_bounds ) )
      71              :     {
      72              :         /* it does not matter how big a classifier is - being outside is a high debt: */
      73            0 :         debts += LAYOUT_QUALITY_WEIGHT_NOT_IN_DIAGRAM_AREA * geometry_rectangle_get_area ( diagram_draw_area );
      74              :     }
      75              : 
      76              :     /* add move distance as debt */
      77            0 :     debts += LAYOUT_QUALITY_WEIGHT_LOCATION * fabs( geometry_offset_get_dx( order_target ) );
      78            0 :     debts += LAYOUT_QUALITY_WEIGHT_LOCATION * fabs( geometry_offset_get_dy( order_target ) );
      79              : 
      80            0 :     return debts;
      81              : }
      82              : 
      83            0 : static inline double layout_quality_debts_class_class( const layout_quality_t *this_,
      84              :                                                        const layout_visible_classifier_t *probe,
      85              :                                                        const layout_visible_classifier_t *other,
      86              :                                                        const layout_visible_set_t *layout_data )
      87              : {
      88            0 :     assert( probe != NULL );
      89            0 :     assert( other != NULL );
      90            0 :     assert( layout_data != NULL );
      91            0 :     double debts = 0.0;
      92              : 
      93              :     const geometry_rectangle_t *const probe_symbol_box
      94            0 :         = layout_visible_classifier_get_symbol_box_const( probe );
      95              :     const geometry_rectangle_t *const other_symbol_box
      96            0 :         = layout_visible_classifier_get_symbol_box_const( other );
      97              : 
      98              :     geometry_rectangle_t probe_intersect;
      99              :     const int intersect_err
     100            0 :         = geometry_rectangle_init_by_intersect( &probe_intersect, probe_symbol_box, other_symbol_box );
     101            0 :     if ( 0 == intersect_err )
     102              :     {
     103              :         /* there is an intersect */
     104            0 :         if ( layout_visible_set_is_ancestor( layout_data, probe, other ) )
     105              :         {
     106              :             /* no debt: parent my overlap children */
     107              :         }
     108            0 :         else if ( layout_visible_set_is_ancestor( layout_data, other, probe ) )
     109              :         {
     110              :             /* no debt: child may overlap parent */
     111              :         }
     112              :         else
     113              :         {
     114            0 :             const double probe_intersect_area = geometry_rectangle_get_area ( &probe_intersect );
     115            0 :             debts += LAYOUT_QUALITY_WEIGHT_CROSS_AREAS * probe_intersect_area;
     116              :         }
     117              :     }
     118              :     /* else no intersect/overlap of symbol box */
     119              : 
     120              :     /* independant of relationship between classifiers, overlapping labels are not good */
     121              :     const geometry_rectangle_t *const probe_icon_box
     122            0 :         = layout_visible_classifier_get_icon_box_const( probe );
     123              :     const geometry_rectangle_t *const probe_label_box
     124            0 :         = layout_visible_classifier_get_label_box_const( probe );
     125            0 :     debts += layout_quality_debts_label_class( this_, probe_icon_box, other );
     126            0 :     debts += layout_quality_debts_label_class( this_, probe_label_box, other );
     127              : 
     128            0 :     return debts;
     129              : }
     130              : 
     131              : static const geometry_3dir_t LAYOUT_QUALITY_BAD_V_PATTERN1
     132              :     = { .first = GEOMETRY_DIRECTION_LEFT,  .second = GEOMETRY_DIRECTION_DOWN,  .third = GEOMETRY_DIRECTION_LEFT };
     133              : static const geometry_3dir_t LAYOUT_QUALITY_BAD_V_PATTERN2
     134              :     = { .first = GEOMETRY_DIRECTION_RIGHT, .second = GEOMETRY_DIRECTION_UP,    .third = GEOMETRY_DIRECTION_RIGHT };
     135              : static const geometry_3dir_t LAYOUT_QUALITY_BAD_H_PATTERN1
     136              :     = { .first = GEOMETRY_DIRECTION_DOWN,  .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_DOWN };
     137              : static const geometry_3dir_t LAYOUT_QUALITY_BAD_H_PATTERN2
     138              :     = { .first = GEOMETRY_DIRECTION_UP,    .second = GEOMETRY_DIRECTION_LEFT,  .third = GEOMETRY_DIRECTION_UP };
     139              : 
     140            0 : static inline double layout_quality_debts_conn_diag( const layout_quality_t *this_,
     141              :                                                      const geometry_connector_t *probe,
     142              :                                                      const geometry_rectangle_t *source_rect,
     143              :                                                      const geometry_rectangle_t *dest_rect,
     144              :                                                      const layout_diagram_t *other )
     145              : {
     146            0 :     assert( probe != NULL );
     147            0 :     assert( source_rect != NULL );
     148            0 :     assert( dest_rect != NULL );
     149            0 :     assert( other != NULL );
     150            0 :     double debts = 0.0;
     151              : 
     152              :     /* get information on probe */
     153              :     const geometry_rectangle_t connector_bounds
     154            0 :         = geometry_connector_get_bounding_rectangle( probe );
     155            0 :     const double length = geometry_connector_get_length( probe );
     156              : 
     157              :     /* get information on expected source and destination */
     158            0 :     const double src_center_x = geometry_rectangle_get_center_x ( source_rect );
     159            0 :     const double src_center_y = geometry_rectangle_get_center_y ( source_rect );
     160            0 :     const double dst_center_x = geometry_rectangle_get_center_x ( dest_rect );
     161            0 :     const double dst_center_y = geometry_rectangle_get_center_y ( dest_rect );
     162              : 
     163              :     /* get draw area */
     164              :     const geometry_rectangle_t *const diagram_draw_area
     165            0 :         = layout_diagram_get_draw_area_const( other );
     166            0 :     const double diagram_draw_center_x = geometry_rectangle_get_center_x( diagram_draw_area );
     167            0 :     const double diagram_draw_center_y = geometry_rectangle_get_center_y( diagram_draw_area );
     168              : 
     169              :     /* get preferred object distance and line-corrdor width */
     170            0 :     const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     171            0 :     const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     172              : 
     173              :     /* add debts for exceeding the diagram draw area */
     174            0 :     if ( ! geometry_rectangle_is_containing( diagram_draw_area, &connector_bounds ) )
     175              :     {
     176              :         /* high debt */
     177            0 :         debts += LAYOUT_QUALITY_WEIGHT_NOT_IN_DIAGRAM_AREA * geometry_rectangle_get_area(diagram_draw_area);
     178              :     }
     179              : 
     180              :     /* the more length, the more unwanted... */
     181            0 :     debts += LAYOUT_QUALITY_WEIGHT_DISTANCE * length * line_corridor;
     182              : 
     183              :     /* prefer _either_ no _or_ minimum-dist lengths of parts, otherwise line too close to object... */
     184            0 :     const double source_length = geometry_connector_get_source_length( probe );
     185            0 :     if (( source_length > 0.000001 )&&( source_length < object_dist ))
     186              :     {
     187            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * ( object_dist - source_length ) * line_corridor;
     188              : 
     189              :     }
     190            0 :     const double destination_length = geometry_connector_get_destination_length( probe );
     191            0 :     if (( destination_length > 0.000001 )&&( destination_length < object_dist ))
     192              :     {
     193            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * ( object_dist - destination_length ) * line_corridor;
     194              :     }
     195              :     /* prefer _either_ no _or_ minimum-dist lengths of main if only one of of source and dest exists */
     196            0 :     const bool no_source_or_no_dest = ( source_length < 0.000001 )||( destination_length < 0.000001 );
     197            0 :     const double main_length = geometry_connector_get_main_length( probe );
     198            0 :     if (( main_length > 0.000001 )&&( main_length < object_dist )&&( no_source_or_no_dest ))
     199              :     {
     200            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * ( object_dist - main_length ) * line_corridor;
     201              :     }
     202              : 
     203              :     /* if the object distance is too low, prefer a detour */
     204            0 :     const double minimum_good_length = 2.0 * object_dist;  /* not more than 2.0 because interfaces at components are rather close... */
     205            0 :     if (( length < minimum_good_length ))
     206              :     {
     207            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * ( minimum_good_length - length ) * line_corridor;
     208              :     }
     209              : 
     210              :     /* prefer centered over uncentered departure and arrival */
     211              :     const double delta_source
     212            0 :         = fmin( fabs( geometry_connector_get_source_end_x( probe ) - src_center_x ),
     213            0 :                 fabs( geometry_connector_get_source_end_y( probe ) - src_center_y ) );
     214            0 :     debts += LAYOUT_QUALITY_WEIGHT_LOCATION * delta_source * ( line_corridor );
     215              :     const double delta_destination
     216            0 :         = fmin( fabs( geometry_connector_get_destination_end_x( probe ) - dst_center_x ),
     217            0 :                 fabs( geometry_connector_get_destination_end_y( probe ) - dst_center_y ) );
     218            0 :     debts += LAYOUT_QUALITY_WEIGHT_LOCATION * delta_destination * ( line_corridor );
     219              : 
     220              :     /* prefer left-hand angles over right-handed */
     221            0 :     const geometry_3dir_t pattern = geometry_connector_get_directions( probe );
     222            0 :     const bool bad_pattern_v
     223            0 :         = geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_V_PATTERN1 )
     224            0 :         || geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_V_PATTERN2 );
     225            0 :     const bool bad_pattern_h
     226            0 :         = geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_H_PATTERN1 )
     227            0 :         || geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_H_PATTERN2 );
     228            0 :     if ( bad_pattern_h || bad_pattern_v )
     229              :     {
     230            0 :         const double current_len = length;
     231            0 :         if ( current_len > ( 4.0 * object_dist ) )
     232              :         {
     233              :             /* probe is a long path and right-handed */
     234              :             /* to avoid overreactions, we assume a line width of 0.1 only */
     235            0 :             debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * ( 0.1 * line_corridor );
     236              :         }
     237              :     }
     238              : 
     239              :     /* to avoid bad patterns: no L on top-left, no 7 on bottom-right, no r on top-right, no J on bottom-left */
     240              :     {
     241            0 :         const bool connector_is_left
     242            0 :         = geometry_rectangle_get_center_x( &connector_bounds ) < diagram_draw_center_x;
     243            0 :         const bool connector_is_top
     244            0 :         = geometry_rectangle_get_center_y( &connector_bounds ) < diagram_draw_center_y;
     245            0 :         if ( connector_is_left )
     246              :         {
     247            0 :             if ( connector_is_top )
     248              :             {
     249              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_L_PATTERN1
     250              :                     = { .first = GEOMETRY_DIRECTION_LEFT,  .second = GEOMETRY_DIRECTION_UP,     .third = GEOMETRY_DIRECTION_CENTER };
     251              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_L_PATTERN2
     252              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_LEFT,  .third = GEOMETRY_DIRECTION_UP };
     253              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_L_PATTERN3
     254              :                     = { .first = GEOMETRY_DIRECTION_DOWN,   .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_CENTER };
     255              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_L_PATTERN4
     256              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_DOWN,  .third = GEOMETRY_DIRECTION_RIGHT };
     257              : 
     258            0 :                 if (( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_L_PATTERN1 ) )
     259            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_L_PATTERN2 ) )
     260            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_L_PATTERN3 ) )
     261            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_L_PATTERN4 ) ))
     262              :                 {
     263            0 :                     debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
     264              :                 }
     265              :             }
     266              :             else
     267              :             {
     268              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_J_PATTERN1
     269              :                     = { .first = GEOMETRY_DIRECTION_DOWN,   .second = GEOMETRY_DIRECTION_LEFT,  .third = GEOMETRY_DIRECTION_CENTER };
     270              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_J_PATTERN2
     271              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_DOWN,  .third = GEOMETRY_DIRECTION_LEFT };
     272              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_J_PATTERN3
     273              :                     = { .first = GEOMETRY_DIRECTION_RIGHT,  .second = GEOMETRY_DIRECTION_UP,    .third = GEOMETRY_DIRECTION_CENTER };
     274              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_J_PATTERN4
     275              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_UP };
     276              : 
     277            0 :                 if (( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_J_PATTERN1 ) )
     278            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_J_PATTERN2 ) )
     279            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_J_PATTERN3 ) )
     280            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_J_PATTERN4 ) ))
     281              :                 {
     282            0 :                     debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
     283              :                 }
     284              :             }
     285              :         }
     286              :         else
     287              :         {
     288            0 :             if ( connector_is_top )
     289              :             {
     290              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_r_PATTERN1
     291              :                     = { .first = GEOMETRY_DIRECTION_UP,     .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_CENTER };
     292              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_r_PATTERN2
     293              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_UP,    .third = GEOMETRY_DIRECTION_RIGHT };
     294              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_r_PATTERN3
     295              :                     = { .first = GEOMETRY_DIRECTION_LEFT,   .second = GEOMETRY_DIRECTION_DOWN,  .third = GEOMETRY_DIRECTION_CENTER };
     296              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_r_PATTERN4
     297              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_LEFT,  .third = GEOMETRY_DIRECTION_DOWN };
     298              : 
     299            0 :                 if (( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_r_PATTERN1 ) )
     300            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_r_PATTERN2 ) )
     301            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_r_PATTERN3 ) )
     302            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_r_PATTERN4 ) ))
     303              :                 {
     304            0 :                     debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
     305              :                 }
     306              :             }
     307              :             else
     308              :             {
     309              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_7_PATTERN1
     310              :                     = { .first = GEOMETRY_DIRECTION_RIGHT,  .second = GEOMETRY_DIRECTION_DOWN,  .third = GEOMETRY_DIRECTION_CENTER };
     311              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_7_PATTERN2
     312              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_DOWN };
     313              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_7_PATTERN3
     314              :                     = { .first = GEOMETRY_DIRECTION_UP,     .second = GEOMETRY_DIRECTION_LEFT,  .third = GEOMETRY_DIRECTION_CENTER };
     315              :                 static const geometry_3dir_t LAYOUT_QUALITY_BAD_7_PATTERN4
     316              :                     = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_UP,    .third = GEOMETRY_DIRECTION_LEFT };
     317              : 
     318            0 :                 if (( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_7_PATTERN1 ) )
     319            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_7_PATTERN2 ) )
     320            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_7_PATTERN3 ) )
     321            0 :                     || ( geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_7_PATTERN4 ) ))
     322              :                 {
     323            0 :                     debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
     324              :                 }
     325              :             }
     326              :         }
     327              :     }
     328              : 
     329            0 :     return debts;
     330              : }
     331              : 
     332            0 : static inline double layout_quality_debts_conn_class ( const layout_quality_t *this_,
     333              :                                                        const geometry_connector_t *probe,
     334              :                                                        const layout_visible_classifier_t *other,
     335              :                                                        const bool is_source,
     336              :                                                        const bool is_ancestor_of_source,
     337              :                                                        const bool is_destination,
     338              :                                                        const bool is_ancestor_of_destination )
     339              : {
     340            0 :     assert( probe != NULL );
     341            0 :     assert( other != NULL );
     342            0 :     double debts = 0.0;
     343              : 
     344              :     const geometry_rectangle_t connector_bounds
     345            0 :         = geometry_connector_get_bounding_rectangle( probe );
     346              :     const geometry_rectangle_t *const classifier_space
     347            0 :         = layout_visible_classifier_get_space_const( other );
     348              : 
     349            0 :     if ( ! geometry_rectangle_is_containing( classifier_space, &connector_bounds ) )
     350              :     {
     351            0 :         const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     352            0 :         const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
     353              : 
     354              :         const geometry_rectangle_t *const classifier_symbol_box
     355            0 :             = layout_visible_classifier_get_symbol_box_const( other );
     356            0 :         if ( is_source && is_destination )
     357              :         {
     358              :             /* do not care if connector is inside or outside */
     359              :         }
     360            0 :         else if ( ( is_ancestor_of_source || is_source ) && ( is_ancestor_of_destination || is_destination ) )
     361            0 :         {
     362              :             /* probe is ancestor of both, do not leave the classifiers space area */
     363            0 :             const double unwanted_detour
     364            0 :                 = geometry_connector_get_length( probe ) - geometry_connector_get_transit_length( probe, classifier_space );
     365            0 :             debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA * unwanted_detour * line_corridor;
     366              :         }
     367            0 :         else if ( ( ! is_ancestor_of_source )&&( ! is_ancestor_of_destination ) )
     368              :         {
     369              :             /* probe is no ancestor of source or destination */
     370            0 :             debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
     371            0 :                 * geometry_connector_get_transit_length( probe, classifier_symbol_box ) * line_corridor;
     372              :         }
     373              :         const double same_path
     374            0 :             = geometry_connector_get_same_path_length_rect( probe,
     375              :                                                             classifier_symbol_box,
     376              :                                                             5.0 * line_width
     377              :                                                           );
     378              :         /* ^ max_distance is 5x line width because the contour line of a classifier is drawn at 3x linewidth within the bounds */
     379            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
     380              : 
     381              :         const geometry_rectangle_t *const classifier_icon_box
     382            0 :             = layout_visible_classifier_get_icon_box_const( other );
     383            0 :         debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE
     384            0 :             * geometry_connector_get_transit_length( probe, classifier_icon_box ) * line_corridor;
     385              : 
     386              :         const geometry_rectangle_t *const classifier_label_box
     387            0 :             = layout_visible_classifier_get_label_box_const( other );
     388            0 :         debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE
     389            0 :             * geometry_connector_get_transit_length( probe, classifier_label_box ) * line_corridor;
     390              :     }
     391              : 
     392            0 :     return debts;
     393              : }
     394              : 
     395            0 : static inline double layout_quality_debts_conn_sym( const layout_quality_t *this_,
     396              :                                                     const geometry_connector_t *probe,
     397              :                                                     const geometry_rectangle_t *other )
     398              : {
     399            0 :     assert( probe != NULL );
     400            0 :     assert( other != NULL );
     401            0 :     double debts = 0.0;
     402              : 
     403            0 :     const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     404            0 :     const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
     405              : 
     406            0 :     debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
     407            0 :         * geometry_connector_get_transit_length( probe, other ) * line_corridor;
     408              :     const double same_path
     409            0 :         = geometry_connector_get_same_path_length_rect( probe, other, 5.0 * line_width );
     410              :     /* ^ max_distance is 5x line width because the contour line of a classifier is 3x linewidth within the bounds */
     411            0 :     debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
     412              : 
     413            0 :     return debts;
     414              : }
     415              : 
     416            0 : static inline double layout_quality_debts_conn_conn( const layout_quality_t *this_,
     417              :                                                      const geometry_connector_t *probe,
     418              :                                                      const geometry_connector_t *other,
     419              :                                                      const bool same_type,
     420              :                                                      const bool same_source,
     421              :                                                      const bool same_destination )
     422              : {
     423            0 :     assert( probe != NULL );
     424            0 :     assert( other != NULL );
     425            0 :     double debts = 0.0;
     426              : 
     427            0 :     const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     428            0 :     const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
     429            0 :     const bool one_same_end = ( same_source != same_destination );
     430              : 
     431              :     const uint32_t intersects
     432            0 :         = geometry_connector_count_connector_intersects( probe, other );
     433            0 :     debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINES * intersects * ( line_corridor * line_corridor );
     434              : 
     435              :     /* if probe and current have same type and (same source classifier xor same destination classifier), overlaps are ok */
     436            0 :     if ( ! ( same_type && one_same_end ) )
     437              :     {
     438              :         const double same_path
     439            0 :             = geometry_connector_get_same_path_length_conn( probe, other, 3.0 * line_width );
     440              :         /* ^ max_distance is 3x line width for 1) own line, 2) minimal gap and 3) other line */
     441            0 :         debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
     442              :     }
     443              : 
     444              :     /* get data on probe */
     445            0 :     const geometry_3dir_t pattern = geometry_connector_get_directions( probe );
     446            0 :     const bool bad_pattern_v
     447            0 :         = geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_V_PATTERN1 )
     448            0 :         || geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_V_PATTERN2 );
     449            0 :     const bool bad_pattern_h
     450            0 :         = geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_H_PATTERN1 )
     451            0 :         || geometry_3dir_equals( &pattern, &LAYOUT_QUALITY_BAD_H_PATTERN2 );
     452              : 
     453            0 :     if ( ( bad_pattern_h || bad_pattern_v ) && ( intersects > 0 ) )
     454              :     {
     455            0 :         const geometry_3dir_t other_pattern = geometry_connector_get_directions( other );
     456            0 :         const bool bad_other_v
     457            0 :             = geometry_3dir_equals( &other_pattern, &LAYOUT_QUALITY_BAD_V_PATTERN1 )
     458            0 :             || geometry_3dir_equals( &other_pattern, &LAYOUT_QUALITY_BAD_V_PATTERN2 );
     459            0 :         const bool bad_other_h
     460            0 :             = geometry_3dir_equals( &other_pattern, &LAYOUT_QUALITY_BAD_H_PATTERN1 )
     461            0 :             || geometry_3dir_equals( &other_pattern, &LAYOUT_QUALITY_BAD_H_PATTERN2 );
     462            0 :         if (( bad_pattern_h && bad_other_v )||( bad_pattern_v && bad_other_h ))
     463              :         {
     464              :             const geometry_rectangle_t probe_bounds
     465            0 :                 = geometry_connector_get_bounding_rectangle( probe );
     466              :             const geometry_rectangle_t other_bounds
     467            0 :                 = geometry_connector_get_bounding_rectangle( other );
     468              : 
     469            0 :             debts += LAYOUT_QUALITY_WEIGHT_FORBIDDEN * geometry_rectangle_get_area( &probe_bounds );
     470            0 :             debts += LAYOUT_QUALITY_WEIGHT_FORBIDDEN * geometry_rectangle_get_area( &other_bounds );
     471              :         }
     472              :     }
     473              : 
     474            0 :     return debts;
     475              : }
     476              : 
     477            0 : static inline double layout_quality_debts_label_diag( const layout_quality_t *this_,
     478              :                                                       const geometry_rectangle_t *probe,
     479              :                                                       const geometry_point_t *target_point,
     480              :                                                       const layout_diagram_t *other )
     481              : {
     482            0 :     assert( probe != NULL );
     483            0 :     assert( target_point != NULL );
     484            0 :     assert( other != NULL );
     485            0 :     double debts = 0.0;
     486              : 
     487              :     const geometry_rectangle_t *const diagram_draw_area
     488            0 :         = layout_diagram_get_draw_area_const( other );
     489              : 
     490            0 :     const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     491              : 
     492              :     /* check distance to target point */
     493              : #if 0
     494              :     const geometry_point_t probe_middle = geometry_rectangle_get_center( probe );
     495              :     debts += LAYOUT_QUALITY_WEIGHT_DISTANCE * geometry_point_calc_chess_distance ( target_point, &probe_middle ) * line_corridor;
     496              : #endif
     497              :     const double label_to_object_gap
     498            0 :         = geometry_rectangle_calc_chess_distance( probe,
     499              :                                                   geometry_point_get_x( target_point ),
     500              :                                                   geometry_point_get_y( target_point )
     501              :                                                 );
     502            0 :     debts += LAYOUT_QUALITY_WEIGHT_DISTANCE * label_to_object_gap * line_corridor;
     503              : 
     504              :     /* add debts for exceeding the diagram draw area */
     505            0 :     if ( ! geometry_rectangle_is_containing( diagram_draw_area, probe ) )
     506              :     {
     507              :         /* high debt */
     508            0 :         debts += LAYOUT_QUALITY_WEIGHT_NOT_IN_DIAGRAM_AREA * geometry_rectangle_get_area(diagram_draw_area);
     509              :     }
     510              : 
     511            0 :     return debts;
     512              : }
     513              : 
     514            0 : static inline double layout_quality_debts_label_class( const layout_quality_t *this_,
     515              :                                                        const geometry_rectangle_t *probe,
     516              :                                                        const layout_visible_classifier_t *other )
     517              : {
     518            0 :     assert( probe != NULL );
     519            0 :     assert( other != NULL );
     520            0 :     double debts = 0.0;
     521              : 
     522              :     const geometry_rectangle_t *const classifier_symbol_box
     523            0 :         = layout_visible_classifier_get_symbol_box_const( other );
     524            0 :     if ( geometry_rectangle_is_intersecting( probe, classifier_symbol_box ) )
     525              :     {
     526              :         /* overlaps to the symbol box are bad only if not contained in space area */
     527              :         const geometry_rectangle_t *const classifier_space
     528            0 :             = layout_visible_classifier_get_space_const( other );
     529            0 :         if ( ! geometry_rectangle_is_containing( classifier_space, probe ) )
     530              :         {
     531              :             /* lower debt */
     532            0 :             debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE * geometry_rectangle_get_intersect_area( probe, classifier_symbol_box );
     533              :         }
     534              :     }
     535              : 
     536              :     const geometry_rectangle_t *const classifier_label_box
     537            0 :         = layout_visible_classifier_get_label_box_const( other );
     538            0 :     if ( geometry_rectangle_is_intersecting( probe, classifier_label_box ) )
     539              :     {
     540              :         /* medium debt */
     541            0 :         debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, classifier_label_box );
     542              :     }
     543              : 
     544              :     const geometry_rectangle_t *const classifier_icon_box
     545            0 :     = layout_visible_classifier_get_icon_box_const( other );
     546            0 :     if ( geometry_rectangle_is_intersecting( probe, classifier_icon_box ) )
     547              :     {
     548              :         /* medium debt */
     549            0 :         debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, classifier_icon_box );
     550              :     }
     551              : 
     552            0 :     return debts;
     553              : }
     554              : 
     555            0 : static inline double layout_quality_debts_label_feat( const layout_quality_t *this_,
     556              :                                                       const geometry_rectangle_t *probe,
     557              :                                                       const layout_feature_t *other )
     558              : {
     559            0 :     assert( probe != NULL );
     560            0 :     assert( other != NULL );
     561            0 :     double debts = 0.0;
     562              : 
     563              :     /* special handling for lifelines needed because labels are line-breaked ignoring lifelines. */
     564              :     /* If optimizing the layout ignoring lifelines and then selecting a solution considering lifelines, */
     565              :     /* then we do not even hit a local optimum. */
     566            0 :     const data_feature_t *const other_data = layout_feature_get_data_const( other );
     567            0 :     if ( DATA_FEATURE_TYPE_LIFELINE != data_feature_get_main_type( other_data ) )
     568              :     {
     569              : 
     570              :         const geometry_rectangle_t *const feature_symbol_box
     571            0 :             = layout_feature_get_symbol_box_const( other );
     572            0 :         if ( geometry_rectangle_is_intersecting( probe, feature_symbol_box ) )
     573              :         {
     574            0 :             debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE * geometry_rectangle_get_intersect_area( probe, feature_symbol_box );
     575              :         }
     576              :     }
     577              : 
     578              :     const geometry_rectangle_t *const feature_label_box
     579            0 :         = layout_feature_get_label_box_const( other );
     580            0 :     if ( geometry_rectangle_is_intersecting( probe, feature_label_box ) )
     581              :     {
     582              :         /* medium debt */
     583            0 :         debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, feature_label_box );
     584              :     }
     585              : 
     586            0 :     return debts;
     587              : }
     588              : 
     589            0 : static inline double layout_quality_debts_label_rel( const layout_quality_t *this_,
     590              :                                                      const geometry_rectangle_t *probe,
     591              :                                                      const layout_relationship_t *other )
     592              : {
     593            0 :     assert( probe != NULL );
     594            0 :     assert( other != NULL );
     595            0 :     double debts = 0.0;
     596              : 
     597            0 :     if (( PENCIL_VISIBILITY_SHOW == layout_relationship_get_visibility( other ) )
     598            0 :         || ( PENCIL_VISIBILITY_GRAY_OUT == layout_relationship_get_visibility( other ) ))
     599              :     {
     600            0 :         const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
     601              : 
     602              :         const geometry_connector_t *const other_shape
     603            0 :             = layout_relationship_get_shape_const( other );
     604            0 :         debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
     605            0 :             * geometry_connector_get_transit_length( other_shape, probe ) * line_corridor;
     606              : 
     607              :         const geometry_rectangle_t *const relationship_label_box
     608            0 :             = layout_relationship_get_label_box_const( other );
     609            0 :         if ( geometry_rectangle_is_intersecting( probe, relationship_label_box ) )
     610              :         {
     611              :             /* medium debt */
     612            0 :             debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, relationship_label_box );
     613              :         }
     614              :     }
     615              : 
     616            0 :     return debts;
     617              : }
     618              : 
     619              : 
     620              : /*
     621              : Copyright 2017-2026 Andreas Warnke
     622              : 
     623              : Licensed under the Apache License, Version 2.0 (the "License");
     624              : you may not use this file except in compliance with the License.
     625              : You may obtain a copy of the License at
     626              : 
     627              :     http://www.apache.org/licenses/LICENSE-2.0
     628              : 
     629              : Unless required by applicable law or agreed to in writing, software
     630              : distributed under the License is distributed on an "AS IS" BASIS,
     631              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     632              : See the License for the specific language governing permissions and
     633              : limitations under the License.
     634              : */
        

Generated by: LCOV version 2.0-1