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 PENCIL_BAD_V_PATTERN1
125 : = { .first = GEOMETRY_DIRECTION_LEFT, .second = GEOMETRY_DIRECTION_DOWN, .third = GEOMETRY_DIRECTION_LEFT };
126 : static const geometry_3dir_t PENCIL_BAD_V_PATTERN2
127 : = { .first = GEOMETRY_DIRECTION_RIGHT, .second = GEOMETRY_DIRECTION_UP, .third = GEOMETRY_DIRECTION_RIGHT };
128 : static const geometry_3dir_t PENCIL_BAD_H_PATTERN1
129 : = { .first = GEOMETRY_DIRECTION_DOWN, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_DOWN };
130 : static const geometry_3dir_t PENCIL_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, &PENCIL_BAD_V_PATTERN1 ) || geometry_3dir_equals( &pattern, &PENCIL_BAD_V_PATTERN2 );
213 0 : const bool bad_pattern_h
214 0 : = geometry_3dir_equals( &pattern, &PENCIL_BAD_H_PATTERN1 ) || geometry_3dir_equals( &pattern, &PENCIL_BAD_H_PATTERN2 );
215 0 : if ( bad_pattern_h || bad_pattern_v )
216 : {
217 0 : const double current_len = length;
218 0 : if ( current_len > ( 4.0 * object_dist ) )
219 : {
220 : /* probe is a long path and right-handed */
221 : /* to avoid overreactions, we assume a line width of 0.1 only */
222 0 : debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * ( 0.1 * line_corridor );
223 : }
224 : }
225 :
226 : /* to avoid bad patterns: no L on top-left, no 7 on bottom-right, no r on top-right, no J on bottom-left */
227 : {
228 0 : const bool connector_is_left
229 0 : = geometry_rectangle_get_center_x( &connector_bounds ) < diagram_draw_center_x;
230 0 : const bool connector_is_top
231 0 : = geometry_rectangle_get_center_y( &connector_bounds ) < diagram_draw_center_y;
232 0 : if ( connector_is_left )
233 : {
234 0 : if ( connector_is_top )
235 : {
236 : static const geometry_3dir_t PENCIL_BAD_L_PATTERN1
237 : = { .first = GEOMETRY_DIRECTION_LEFT, .second = GEOMETRY_DIRECTION_UP, .third = GEOMETRY_DIRECTION_CENTER };
238 : static const geometry_3dir_t PENCIL_BAD_L_PATTERN2
239 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_LEFT, .third = GEOMETRY_DIRECTION_UP };
240 : static const geometry_3dir_t PENCIL_BAD_L_PATTERN3
241 : = { .first = GEOMETRY_DIRECTION_DOWN, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_CENTER };
242 : static const geometry_3dir_t PENCIL_BAD_L_PATTERN4
243 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_DOWN, .third = GEOMETRY_DIRECTION_RIGHT };
244 :
245 0 : if (( geometry_3dir_equals( &pattern, &PENCIL_BAD_L_PATTERN1 ) )
246 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_L_PATTERN2 ) )
247 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_L_PATTERN3 ) )
248 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_L_PATTERN4 ) ))
249 : {
250 0 : debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
251 : }
252 : }
253 : else
254 : {
255 : static const geometry_3dir_t PENCIL_BAD_J_PATTERN1
256 : = { .first = GEOMETRY_DIRECTION_DOWN, .second = GEOMETRY_DIRECTION_LEFT, .third = GEOMETRY_DIRECTION_CENTER };
257 : static const geometry_3dir_t PENCIL_BAD_J_PATTERN2
258 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_DOWN, .third = GEOMETRY_DIRECTION_LEFT };
259 : static const geometry_3dir_t PENCIL_BAD_J_PATTERN3
260 : = { .first = GEOMETRY_DIRECTION_RIGHT, .second = GEOMETRY_DIRECTION_UP, .third = GEOMETRY_DIRECTION_CENTER };
261 : static const geometry_3dir_t PENCIL_BAD_J_PATTERN4
262 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_UP };
263 :
264 0 : if (( geometry_3dir_equals( &pattern, &PENCIL_BAD_J_PATTERN1 ) )
265 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_J_PATTERN2 ) )
266 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_J_PATTERN3 ) )
267 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_J_PATTERN4 ) ))
268 : {
269 0 : debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
270 : }
271 : }
272 : }
273 : else
274 : {
275 0 : if ( connector_is_top )
276 : {
277 : static const geometry_3dir_t PENCIL_BAD_r_PATTERN1
278 : = { .first = GEOMETRY_DIRECTION_UP, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_CENTER };
279 : static const geometry_3dir_t PENCIL_BAD_r_PATTERN2
280 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_UP, .third = GEOMETRY_DIRECTION_RIGHT };
281 : static const geometry_3dir_t PENCIL_BAD_r_PATTERN3
282 : = { .first = GEOMETRY_DIRECTION_LEFT, .second = GEOMETRY_DIRECTION_DOWN, .third = GEOMETRY_DIRECTION_CENTER };
283 : static const geometry_3dir_t PENCIL_BAD_r_PATTERN4
284 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_LEFT, .third = GEOMETRY_DIRECTION_DOWN };
285 :
286 0 : if (( geometry_3dir_equals( &pattern, &PENCIL_BAD_r_PATTERN1 ) )
287 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_r_PATTERN2 ) )
288 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_r_PATTERN3 ) )
289 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_r_PATTERN4 ) ))
290 : {
291 0 : debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
292 : }
293 : }
294 : else
295 : {
296 : static const geometry_3dir_t PENCIL_BAD_7_PATTERN1
297 : = { .first = GEOMETRY_DIRECTION_RIGHT, .second = GEOMETRY_DIRECTION_DOWN, .third = GEOMETRY_DIRECTION_CENTER };
298 : static const geometry_3dir_t PENCIL_BAD_7_PATTERN2
299 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_RIGHT, .third = GEOMETRY_DIRECTION_DOWN };
300 : static const geometry_3dir_t PENCIL_BAD_7_PATTERN3
301 : = { .first = GEOMETRY_DIRECTION_UP, .second = GEOMETRY_DIRECTION_LEFT, .third = GEOMETRY_DIRECTION_CENTER };
302 : static const geometry_3dir_t PENCIL_BAD_7_PATTERN4
303 : = { .first = GEOMETRY_DIRECTION_CENTER, .second = GEOMETRY_DIRECTION_UP, .third = GEOMETRY_DIRECTION_LEFT };
304 :
305 0 : if (( geometry_3dir_equals( &pattern, &PENCIL_BAD_7_PATTERN1 ) )
306 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_7_PATTERN2 ) )
307 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_7_PATTERN3 ) )
308 0 : || ( geometry_3dir_equals( &pattern, &PENCIL_BAD_7_PATTERN4 ) ))
309 : {
310 0 : debts += LAYOUT_QUALITY_WEIGHT_AVOID * length * line_corridor;
311 : }
312 : }
313 : }
314 : }
315 :
316 0 : return debts;
317 : }
318 :
319 0 : static inline double layout_quality_debts_conn_class ( const layout_quality_t *this_,
320 : const geometry_connector_t *probe,
321 : const layout_visible_classifier_t *other,
322 : const bool is_ancestor_of_source,
323 : const bool is_ancestor_of_destination )
324 : {
325 0 : double debts = 0.0;
326 :
327 : const geometry_rectangle_t connector_bounds
328 0 : = geometry_connector_get_bounding_rectangle( probe );
329 : const geometry_rectangle_t *const classifier_space
330 0 : = layout_visible_classifier_get_space_const( other );
331 :
332 0 : if ( ! geometry_rectangle_is_containing( classifier_space, &connector_bounds ) )
333 : {
334 0 : const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
335 0 : const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
336 :
337 : const geometry_rectangle_t *const classifier_symbol_box
338 0 : = layout_visible_classifier_get_symbol_box_const( other );
339 0 : if ( is_ancestor_of_source == is_ancestor_of_destination )
340 : {
341 : /* either probe is no ancestor or ancestor of both */
342 0 : debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
343 0 : * geometry_connector_get_transit_length( probe, classifier_symbol_box ) * line_corridor;
344 : }
345 : const double same_path
346 0 : = geometry_connector_get_same_path_length_rect( probe,
347 : classifier_symbol_box,
348 : 5.0 * line_width
349 : );
350 : /* ^ max_distance is 5x line width because the contour line of a classifier is drawn at 3x linewidth within the bounds */
351 0 : debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
352 :
353 : const geometry_rectangle_t *const classifier_icon_box
354 0 : = layout_visible_classifier_get_icon_box_const( other );
355 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE
356 0 : * geometry_connector_get_transit_length( probe, classifier_icon_box ) * line_corridor;
357 :
358 : const geometry_rectangle_t *const classifier_label_box
359 0 : = layout_visible_classifier_get_label_box_const( other );
360 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE
361 0 : * geometry_connector_get_transit_length( probe, classifier_label_box ) * line_corridor;
362 : }
363 :
364 0 : return debts;
365 : }
366 :
367 0 : static inline double layout_quality_debts_conn_sym( const layout_quality_t *this_,
368 : const geometry_connector_t *probe,
369 : const geometry_rectangle_t *other )
370 : {
371 0 : double debts = 0.0;
372 :
373 0 : const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
374 0 : const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
375 :
376 0 : debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
377 0 : * geometry_connector_get_transit_length( probe, other ) * line_corridor;
378 : const double same_path
379 0 : = geometry_connector_get_same_path_length_rect( probe, other, 5.0 * line_width );
380 : /* ^ max_distance is 5x line width because the contour line of a classifier is 3x linewidth within the bounds */
381 0 : debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
382 :
383 0 : return debts;
384 : }
385 :
386 0 : static inline double layout_quality_debts_conn_conn( const layout_quality_t *this_,
387 : const geometry_connector_t *probe,
388 : const geometry_connector_t *other )
389 : {
390 0 : double debts = 0.0;
391 :
392 0 : const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
393 0 : const double line_width = pencil_size_get_standard_line_width( (*this_).pencil_size );
394 :
395 : /* get data on probe */
396 0 : const geometry_3dir_t pattern = geometry_connector_get_directions( probe );
397 0 : const bool bad_pattern_v
398 0 : = geometry_3dir_equals( &pattern, &PENCIL_BAD_V_PATTERN1 ) || geometry_3dir_equals( &pattern, &PENCIL_BAD_V_PATTERN2 );
399 0 : const bool bad_pattern_h
400 0 : = geometry_3dir_equals( &pattern, &PENCIL_BAD_H_PATTERN1 ) || geometry_3dir_equals( &pattern, &PENCIL_BAD_H_PATTERN2 );
401 :
402 : const uint32_t intersects
403 0 : = geometry_connector_count_connector_intersects( probe, other );
404 0 : debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINES * intersects * ( line_corridor * line_corridor );
405 : const double same_path
406 0 : = geometry_connector_get_same_path_length_conn( probe, other, 3.0 * line_width );
407 : /* ^ max_distance is 3x line width for 1) own line, 2) minimal gap and 3) other line */
408 0 : debts += LAYOUT_QUALITY_WEIGHT_SHARED_LINES * same_path * line_corridor;
409 :
410 0 : if ( ( bad_pattern_h || bad_pattern_v ) && ( intersects > 0 ) )
411 : {
412 0 : const geometry_3dir_t other_pattern = geometry_connector_get_directions( other );
413 0 : const bool bad_other_v
414 0 : = geometry_3dir_equals( &other_pattern, &PENCIL_BAD_V_PATTERN1 )
415 0 : || geometry_3dir_equals( &other_pattern, &PENCIL_BAD_V_PATTERN2 );
416 0 : const bool bad_other_h
417 0 : = geometry_3dir_equals( &other_pattern, &PENCIL_BAD_H_PATTERN1 )
418 0 : || geometry_3dir_equals( &other_pattern, &PENCIL_BAD_H_PATTERN2 );
419 0 : if (( bad_pattern_h && bad_other_v )||( bad_pattern_v && bad_other_h ))
420 : {
421 : const geometry_rectangle_t probe_bounds
422 0 : = geometry_connector_get_bounding_rectangle( probe );
423 : const geometry_rectangle_t other_bounds
424 0 : = geometry_connector_get_bounding_rectangle( other );
425 :
426 0 : debts += LAYOUT_QUALITY_WEIGHT_FORBIDDEN * geometry_rectangle_get_area( &probe_bounds );
427 0 : debts += LAYOUT_QUALITY_WEIGHT_FORBIDDEN * geometry_rectangle_get_area( &other_bounds );
428 : }
429 : }
430 :
431 0 : return debts;
432 : }
433 :
434 0 : static inline double layout_quality_debts_label_diag( const layout_quality_t *this_,
435 : const geometry_rectangle_t *probe,
436 : const geometry_point_t *target_point,
437 : const layout_diagram_t *other )
438 : {
439 0 : double debts = 0.0;
440 :
441 : const geometry_rectangle_t *const diagram_draw_area
442 0 : = layout_diagram_get_draw_area_const( other );
443 :
444 : /* check distance to target point */
445 0 : const geometry_point_t probe_middle = geometry_rectangle_get_center( probe );
446 0 : debts += LAYOUT_QUALITY_WEIGHT_DISTANCE * geometry_point_calc_chess_distance ( target_point, &probe_middle );
447 :
448 : /* add debts for exceeding the diagram draw area */
449 0 : if ( ! geometry_rectangle_is_containing( diagram_draw_area, probe ) )
450 : {
451 : /* high debt */
452 0 : debts += LAYOUT_QUALITY_WEIGHT_NOT_IN_DIAGRAM_AREA * geometry_rectangle_get_area(diagram_draw_area);
453 : }
454 :
455 0 : return debts;
456 : }
457 :
458 0 : static inline double layout_quality_debts_label_class( const layout_quality_t *this_,
459 : const geometry_rectangle_t *probe,
460 : const layout_visible_classifier_t *other )
461 : {
462 0 : double debts = 0.0;
463 :
464 : const geometry_rectangle_t *const classifier_symbol_box
465 0 : = layout_visible_classifier_get_symbol_box_const( other );
466 0 : if ( geometry_rectangle_is_intersecting( probe, classifier_symbol_box ) )
467 : {
468 : /* overlaps to the symbol box are bad only if not contained in space area */
469 : const geometry_rectangle_t *const classifier_space
470 0 : = layout_visible_classifier_get_space_const( other );
471 0 : if ( ! geometry_rectangle_is_containing( classifier_space, probe ) )
472 : {
473 : /* lower debt */
474 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE * geometry_rectangle_get_intersect_area( probe, classifier_symbol_box );
475 : }
476 : }
477 :
478 : const geometry_rectangle_t *const classifier_label_box
479 0 : = layout_visible_classifier_get_label_box_const( other );
480 0 : if ( geometry_rectangle_is_intersecting( probe, classifier_label_box ) )
481 : {
482 : /* medium debt */
483 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, classifier_label_box );
484 : }
485 :
486 : const geometry_rectangle_t *const classifier_icon_box
487 0 : = layout_visible_classifier_get_icon_box_const( other );
488 0 : if ( geometry_rectangle_is_intersecting( probe, classifier_icon_box ) )
489 : {
490 : /* medium debt */
491 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, classifier_icon_box );
492 : }
493 :
494 0 : return debts;
495 : }
496 :
497 0 : static inline double layout_quality_debts_label_feat( const layout_quality_t *this_,
498 : const geometry_rectangle_t *probe,
499 : const layout_feature_t *other )
500 : {
501 0 : double debts = 0.0;
502 :
503 : const geometry_rectangle_t *const feature_symbol_box
504 0 : = layout_feature_get_symbol_box_const( other );
505 0 : if ( geometry_rectangle_is_intersecting( probe, feature_symbol_box ) )
506 : {
507 : /* no special handling for lifelines */
508 : /* const data_feature_t *const probe_f_data = layout_feature_get_data_const( other ); */
509 : /* if ( DATA_FEATURE_TYPE_LIFELINE == data_feature_get_main_type( probe_f_data ) ) { } else { } */
510 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_ON_LINE * geometry_rectangle_get_intersect_area( probe, feature_symbol_box );
511 : }
512 :
513 : const geometry_rectangle_t *const feature_label_box
514 0 : = layout_feature_get_label_box_const( other );
515 0 : if ( geometry_rectangle_is_intersecting( probe, feature_label_box ) )
516 : {
517 : /* medium debt */
518 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, feature_label_box );
519 : }
520 :
521 0 : return debts;
522 : }
523 :
524 0 : static inline double layout_quality_debts_label_rel( const layout_quality_t *this_,
525 : const geometry_rectangle_t *probe,
526 : const layout_relationship_t *other )
527 : {
528 0 : double debts = 0.0;
529 :
530 0 : if (( PENCIL_VISIBILITY_SHOW == layout_relationship_get_visibility( other ) )
531 0 : || ( PENCIL_VISIBILITY_GRAY_OUT == layout_relationship_get_visibility( other ) ))
532 : {
533 0 : const double line_corridor = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
534 :
535 : const geometry_connector_t *const other_shape
536 0 : = layout_relationship_get_shape_const( other );
537 0 : debts += LAYOUT_QUALITY_WEIGHT_CROSS_LINE_AREA
538 0 : * geometry_connector_get_transit_length( other_shape, probe ) * line_corridor;
539 :
540 : const geometry_rectangle_t *const relationship_label_box
541 0 : = layout_relationship_get_label_box_const( other );
542 0 : if ( geometry_rectangle_is_intersecting( probe, relationship_label_box ) )
543 : {
544 : /* medium debt */
545 0 : debts += LAYOUT_QUALITY_WEIGHT_LABEL_OVERLAP * geometry_rectangle_get_intersect_area( probe, relationship_label_box );
546 : }
547 : }
548 :
549 0 : return debts;
550 : }
551 :
552 :
553 : /*
554 : Copyright 2017-2025 Andreas Warnke
555 :
556 : Licensed under the Apache License, Version 2.0 (the "License");
557 : you may not use this file except in compliance with the License.
558 : You may obtain a copy of the License at
559 :
560 : http://www.apache.org/licenses/LICENSE-2.0
561 :
562 : Unless required by applicable law or agreed to in writing, software
563 : distributed under the License is distributed on an "AS IS" BASIS,
564 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
565 : See the License for the specific language governing permissions and
566 : limitations under the License.
567 : */
|