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