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 : */
|