Line data Source code
1 : /* File: pencil_relationship_2d_layouter.c; Copyright and License: see below */
2 :
3 : #include "pencil_relationship_2d_layouter.h"
4 : #include "layout/layout_relationship_iter.h"
5 : #include "layout/layout_quality.h"
6 : #include "u8/u8_trace.h"
7 : #include <pango/pangocairo.h>
8 : #include <stdio.h>
9 : #include <stdlib.h>
10 : #include <math.h>
11 : #include <stdint.h>
12 :
13 0 : void pencil_relationship_2d_layouter_init( pencil_relationship_2d_layouter_t *this_,
14 : layout_visible_set_t *layout_data,
15 : const data_profile_part_t *profile,
16 : const pencil_size_t *pencil_size )
17 : {
18 0 : U8_TRACE_BEGIN();
19 0 : assert( NULL != layout_data );
20 0 : assert( NULL != profile );
21 0 : assert( NULL != pencil_size );
22 :
23 0 : (*this_).layout_data = layout_data;
24 0 : (*this_).profile = profile;
25 :
26 0 : universal_array_index_sorter_init( &((*this_).sorted_relationships) );
27 0 : layout_relationship_iter_init( &((*this_).already_processed), layout_data, &((*this_).sorted_relationships) );
28 :
29 0 : (*this_).pencil_size = pencil_size;
30 0 : pencil_relationship_painter_init( &((*this_).relationship_painter) );
31 :
32 0 : U8_TRACE_END();
33 0 : }
34 :
35 0 : void pencil_relationship_2d_layouter_destroy( pencil_relationship_2d_layouter_t *this_ )
36 : {
37 0 : U8_TRACE_BEGIN();
38 :
39 0 : layout_relationship_iter_destroy( &((*this_).already_processed) );
40 0 : universal_array_index_sorter_destroy( &((*this_).sorted_relationships) );
41 :
42 0 : pencil_relationship_painter_destroy( &((*this_).relationship_painter) );
43 :
44 0 : U8_TRACE_END();
45 0 : }
46 :
47 0 : void pencil_relationship_2d_layouter_private_do_layout ( pencil_relationship_2d_layouter_t *this_ )
48 : {
49 0 : U8_TRACE_BEGIN();
50 : assert ( (unsigned int) UNIVERSAL_ARRAY_INDEX_SORTER_MAX_ARRAY_SIZE >= (unsigned int) LAYOUT_VISIBLE_SET_MAX_RELATIONSHIPS );
51 :
52 0 : universal_array_index_sorter_reinit( &((*this_).sorted_relationships) );
53 :
54 : /* sort the relationships by their movement-needs, drop invisible relations */
55 0 : pencil_relationship_2d_layouter_private_propose_processing_order ( this_ );
56 :
57 : /* shape the relationships */
58 : layout_relationship_iter_t relationship_iterator;
59 0 : layout_relationship_iter_init( &relationship_iterator, (*this_).layout_data, &((*this_).sorted_relationships) );
60 0 : while ( layout_relationship_iter_has_next( &relationship_iterator ) )
61 : {
62 : /* initialize the already processed relationship iterator - it is needed by called methods */
63 0 : layout_relationship_iter_destroy( &((*this_).already_processed) );
64 0 : layout_relationship_iter_init_from_processed( &((*this_).already_processed), &relationship_iterator );
65 :
66 : /* determine pointer to relationship */
67 0 : layout_relationship_t *const current_relationship = layout_relationship_iter_next_ptr( &relationship_iterator );
68 :
69 : /* declaration of list of options */
70 0 : uint32_t solutions_count = 0;
71 : static const uint32_t SOLUTIONS_MAX = 18;
72 : geometry_connector_t solution[18];
73 :
74 : /* propose options */
75 0 : pencil_relationship_2d_layouter_private_propose_solutions( this_,
76 : current_relationship,
77 : SOLUTIONS_MAX,
78 : solution,
79 : &solutions_count
80 : );
81 :
82 : /* select best option */
83 : uint32_t index_of_best;
84 0 : if ( 1 == solutions_count )
85 : {
86 0 : index_of_best = 0;
87 : }
88 : else
89 : {
90 0 : pencil_relationship_2d_layouter_private_select_solution( this_,
91 : current_relationship,
92 : solutions_count,
93 : solution,
94 : &index_of_best
95 : );
96 : }
97 :
98 : /* store best option to (*this_).layout_data */
99 0 : layout_relationship_set_shape( current_relationship, &(solution[index_of_best]) );
100 :
101 : /* initialize also the label (to empty), this is updated later */
102 : {
103 : geometry_rectangle_t void_rect;
104 0 : geometry_rectangle_init_empty( &void_rect );
105 0 : layout_relationship_set_label_box( current_relationship, &void_rect );
106 0 : geometry_rectangle_destroy( &void_rect );
107 : }
108 :
109 : }
110 0 : layout_relationship_iter_destroy( &relationship_iterator );
111 :
112 : /* clear the array and iterator of processed relationships */
113 0 : universal_array_index_sorter_reinit( &((*this_).sorted_relationships) );
114 0 : layout_relationship_iter_destroy( &((*this_).already_processed) );
115 0 : layout_relationship_iter_init( &((*this_).already_processed), (*this_).layout_data, &((*this_).sorted_relationships) );
116 :
117 0 : U8_TRACE_END();
118 0 : }
119 :
120 0 : void pencil_relationship_2d_layouter_private_propose_processing_order ( pencil_relationship_2d_layouter_t *this_ )
121 : {
122 0 : U8_TRACE_BEGIN();
123 : assert ( (unsigned int) UNIVERSAL_ARRAY_INDEX_SORTER_MAX_ARRAY_SIZE >= (unsigned int) DATA_VISIBLE_SET_MAX_RELATIONSHIPS );
124 :
125 : /* get draw area */
126 : const layout_diagram_t *const diagram_layout
127 0 : = layout_visible_set_get_diagram_ptr( (*this_).layout_data );
128 : const geometry_rectangle_t *const diagram_draw_area
129 0 : = layout_diagram_get_draw_area_const( diagram_layout );
130 :
131 : /* sort the relationships by their shaping-needs: the less simple, the earlier it shall be processed */
132 : const uint32_t count_relations
133 0 : = layout_visible_set_get_relationship_count ( (*this_).layout_data );
134 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
135 : {
136 : layout_relationship_t *const current_relation
137 0 : = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
138 :
139 0 : int64_t simpleness = 0;
140 :
141 : /* determine simpleness by relationship type */
142 : {
143 : data_relationship_type_t reltype;
144 0 : reltype = data_relationship_get_main_type( layout_relationship_get_data_const ( current_relation ));
145 0 : if (( DATA_RELATIONSHIP_TYPE_UML_DEPENDENCY == reltype )
146 0 : ||( DATA_RELATIONSHIP_TYPE_UML_CONTAINMENT == reltype ))
147 : {
148 : /* containment may be solved by embracing, mere dependencies are unimportant */
149 0 : simpleness += geometry_rectangle_get_width ( diagram_draw_area );
150 : }
151 : }
152 :
153 : /* whatever is not visible is simple */
154 : {
155 0 : if (( PENCIL_VISIBILITY_SHOW != layout_relationship_get_visibility ( current_relation ) )
156 0 : && ( PENCIL_VISIBILITY_GRAY_OUT != layout_relationship_get_visibility ( current_relation ) ))
157 : {
158 0 : simpleness += 2 * geometry_rectangle_get_width ( diagram_draw_area );
159 : }
160 : }
161 :
162 : /* determine simpleness by distance between source and destination */
163 : {
164 : const geometry_rectangle_t *const source_rect
165 0 : = layout_relationship_get_from_box_const ( current_relation );
166 : const geometry_rectangle_t *const dest_rect
167 0 : = layout_relationship_get_to_box_const ( current_relation );
168 :
169 0 : simpleness -= fabs ( geometry_rectangle_get_center_x(source_rect) - geometry_rectangle_get_center_x(dest_rect) );
170 0 : simpleness -= fabs ( geometry_rectangle_get_center_y(source_rect) - geometry_rectangle_get_center_y(dest_rect) );
171 : }
172 :
173 : /* insert relation to sorted array, the simpler the more to the back */
174 : {
175 : int insert_error;
176 0 : insert_error = universal_array_index_sorter_insert( &((*this_).sorted_relationships), index, simpleness );
177 0 : if ( 0 != insert_error )
178 : {
179 0 : U8_LOG_WARNING( "not all relationships are shaped" );
180 : }
181 : }
182 : }
183 :
184 0 : U8_TRACE_END();
185 0 : }
186 :
187 0 : void pencil_relationship_2d_layouter_private_propose_solutions ( pencil_relationship_2d_layouter_t *this_,
188 : const layout_relationship_t *current_relation,
189 : uint32_t solutions_max,
190 : geometry_connector_t out_solutions[],
191 : uint32_t *out_solutions_count )
192 : {
193 0 : U8_TRACE_BEGIN();
194 0 : assert ( NULL != current_relation );
195 0 : assert ( NULL != out_solutions );
196 0 : assert ( NULL != out_solutions_count );
197 0 : assert ( 1 <= solutions_max ); /* general requirement to report at least one option */
198 0 : assert ( 18 <= solutions_max ); /* current implementation requires at least 18 options */
199 :
200 : /* propose connections between source and destination */
201 : {
202 : const geometry_rectangle_t *const source_rect
203 0 : = layout_relationship_get_from_box_const ( current_relation );
204 : const geometry_rectangle_t *const dest_rect
205 0 : = layout_relationship_get_to_box_const ( current_relation );
206 :
207 : uint32_t solutions_by_I;
208 0 : pencil_relationship_2d_layouter_private_connect_rectangles_by_I ( this_,
209 : source_rect,
210 : dest_rect,
211 : solutions_max,
212 : &(out_solutions[0]),
213 : &solutions_by_I
214 : );
215 :
216 : uint32_t solutions_by_ZN;
217 0 : pencil_relationship_2d_layouter_private_connect_rectangles_by_ZN ( this_,
218 : source_rect,
219 : dest_rect,
220 : solutions_max - solutions_by_I,
221 0 : &(out_solutions[solutions_by_I]),
222 : &solutions_by_ZN
223 : );
224 :
225 : uint32_t solutions_by_L7;
226 0 : const uint32_t solutions_by_I_ZN = solutions_by_I + solutions_by_ZN;
227 0 : pencil_relationship_2d_layouter_private_connect_rectangles_by_L7 ( this_,
228 : source_rect,
229 : dest_rect,
230 : solutions_max - solutions_by_I_ZN,
231 0 : &(out_solutions[solutions_by_I_ZN]),
232 : &solutions_by_L7
233 : );
234 :
235 : uint32_t solutions_by_UC;
236 0 : const uint32_t solutions_by_I_ZN_L7 = solutions_by_I_ZN + solutions_by_L7;
237 0 : pencil_relationship_2d_layouter_private_connect_rectangles_by_UC ( this_,
238 : source_rect,
239 : dest_rect,
240 : solutions_max - solutions_by_I_ZN_L7,
241 0 : &(out_solutions[solutions_by_I_ZN_L7]),
242 : &solutions_by_UC
243 : );
244 :
245 0 : *out_solutions_count = solutions_by_I_ZN_L7 + solutions_by_UC;
246 0 : assert ( 1 <= *out_solutions_count );
247 0 : assert ( *out_solutions_count <= solutions_max );
248 : }
249 :
250 0 : U8_TRACE_END();
251 0 : }
252 :
253 0 : void pencil_relationship_2d_layouter_private_select_solution ( pencil_relationship_2d_layouter_t *this_,
254 : const layout_relationship_t *current_relation,
255 : uint32_t solutions_count,
256 : const geometry_connector_t solutions[],
257 : uint32_t *out_index_of_best )
258 : {
259 0 : U8_TRACE_BEGIN();
260 0 : assert ( NULL != current_relation );
261 0 : assert ( NULL != solutions );
262 0 : assert ( NULL != out_index_of_best );
263 0 : assert ( 1 <= solutions_count );
264 :
265 : /* get current relationship data */
266 : const geometry_rectangle_t *const source_rect
267 0 : = layout_relationship_get_from_box_const ( current_relation );
268 : const geometry_rectangle_t *const dest_rect
269 0 : = layout_relationship_get_to_box_const ( current_relation );
270 :
271 : /* get draw area */
272 : const layout_diagram_t *const diagram_layout
273 0 : = layout_visible_set_get_diagram_ptr( (*this_).layout_data );
274 :
275 : /* define potential solution and rating */
276 0 : uint32_t index_of_best = 0;
277 0 : double debts_of_best = DBL_MAX;
278 :
279 : /* evaluate the solutions by their overlaps with classifiers */
280 0 : for ( uint32_t solution_idx = 0; solution_idx < solutions_count; solution_idx ++ )
281 : {
282 0 : const geometry_connector_t *const current_solution = &(solutions[solution_idx]);
283 :
284 : /* avoid alternating solutions in case their debts are identical */
285 0 : double debts_of_current = 0.0;
286 0 : debts_of_current += 0.1 * solution_idx;
287 :
288 : /* evalute the debts of this solution */
289 0 : const layout_quality_t quality = layout_quality_new( (*this_).pencil_size );
290 0 : debts_of_current += layout_quality_debts_conn_diag( &quality, current_solution, source_rect, dest_rect, diagram_layout );
291 :
292 : /* iterate over all classifiers */
293 : const uint32_t count_clasfy
294 0 : = layout_visible_set_get_visible_classifier_count ( (*this_).layout_data );
295 0 : for ( uint32_t clasfy_index = 0; clasfy_index < count_clasfy; clasfy_index ++ )
296 : {
297 : const layout_visible_classifier_t *const probe_classifier
298 0 : = layout_visible_set_get_visible_classifier_ptr( (*this_).layout_data, clasfy_index );
299 0 : const layout_visible_classifier_t *const from = layout_relationship_get_from_classifier_ptr( current_relation );
300 0 : const layout_visible_classifier_t *const to = layout_relationship_get_to_classifier_ptr( current_relation );
301 0 : const bool is_ancestor_of_from = layout_visible_set_is_ancestor( (*this_).layout_data, probe_classifier, from );
302 0 : const bool is_ancestor_of_to = layout_visible_set_is_ancestor( (*this_).layout_data, probe_classifier, to );
303 0 : debts_of_current += layout_quality_debts_conn_class( &quality,
304 : current_solution,
305 : probe_classifier,
306 : is_ancestor_of_from,
307 : is_ancestor_of_to
308 : );
309 : }
310 :
311 : /* iterate over all features, check symbol boxes only, label boxes are not yet initialized */
312 : const uint32_t count_features
313 0 : = layout_visible_set_get_feature_count ( (*this_).layout_data );
314 0 : for ( uint32_t f_idx = 0; f_idx < count_features; f_idx ++ )
315 : {
316 : const layout_feature_t *const feature_layout
317 0 : = layout_visible_set_get_feature_ptr ( (*this_).layout_data, f_idx );
318 :
319 : const geometry_rectangle_t *const feature_symbol_box
320 0 : = layout_feature_get_symbol_box_const( feature_layout );
321 :
322 0 : debts_of_current += layout_quality_debts_conn_sym( &quality, current_solution, feature_symbol_box );
323 : }
324 :
325 : /* iterate over the already created connectors */
326 : layout_relationship_iter_t relationship_iterator;
327 0 : layout_relationship_iter_copy( &relationship_iterator, &((*this_).already_processed) );
328 0 : while ( layout_relationship_iter_has_next( &relationship_iterator ) )
329 : {
330 : /* get pointer to relationship */
331 0 : const layout_relationship_t *const probe_relationship = layout_relationship_iter_next_ptr( &relationship_iterator );
332 :
333 : /* add debts if intersects */
334 : #if 0
335 : const data_relationship_t *const probe_relation_data
336 : = layout_relationship_get_data_const ( probe_relationship );
337 : const bool same_type = ( data_relationship_get_main_type( probe_relation_data )
338 : == data_relationship_get_main_type( current_relation_data ) );
339 : const bool same_from = ( data_relationship_get_from_classifier_row_id( probe_relation_data )
340 : == data_relationship_get_from_classifier_row_id( current_relation_data ) );
341 : const bool same_to = ( data_relationship_get_to_classifier_row_id( probe_relation_data )
342 : == data_relationship_get_to_classifier_row_id( current_relation_data ) );
343 : const bool one_same_end = ( same_from != same_to );
344 : /* if probe and current have same type and (same source classifier xor same destination classifier), overlaps are ok */
345 : if ( ! ( same_type && one_same_end ) )
346 : #endif
347 : {
348 : const geometry_connector_t *const probe_shape
349 0 : = layout_relationship_get_shape_const( probe_relationship );
350 :
351 0 : debts_of_current += layout_quality_debts_conn_conn( &quality, current_solution, probe_shape );
352 : /* U8_TRACE_INFO_INT_INT( "solution[idx] vs probe[idx]", solution_idx, probe_index ); */
353 : }
354 : }
355 0 : layout_relationship_iter_destroy( &relationship_iterator );
356 :
357 : /* update best solution */
358 0 : if ( debts_of_current < debts_of_best )
359 : {
360 0 : index_of_best = solution_idx;
361 0 : debts_of_best = debts_of_current;
362 : }
363 : }
364 :
365 : #if 0
366 : static unsigned int counter = 0;
367 : counter ++;
368 : index_of_best = counter % solutions_count;
369 : #endif
370 :
371 : /* the best */
372 0 : *out_index_of_best = index_of_best;
373 0 : geometry_connector_trace( &(solutions[index_of_best]) );
374 :
375 0 : U8_TRACE_END();
376 0 : }
377 :
378 0 : void pencil_relationship_2d_layouter_private_connect_rectangles_by_I ( pencil_relationship_2d_layouter_t *this_,
379 : const geometry_rectangle_t *source_rect,
380 : const geometry_rectangle_t *dest_rect,
381 : uint32_t solutions_max,
382 : geometry_connector_t out_solutions[],
383 : uint32_t *out_solutions_count )
384 : {
385 0 : U8_TRACE_BEGIN();
386 0 : assert( NULL != source_rect );
387 0 : assert( NULL != dest_rect );
388 0 : assert ( NULL != out_solutions );
389 0 : assert ( NULL != out_solutions_count );
390 0 : assert ( 4 <= solutions_max ); /* current implementation requires at least 4 options */
391 :
392 0 : uint32_t solutions_count = 0;
393 :
394 0 : const double src_left = geometry_rectangle_get_left(source_rect);
395 0 : const double src_right = geometry_rectangle_get_right(source_rect);
396 0 : const double src_top = geometry_rectangle_get_top(source_rect);
397 0 : const double src_bottom = geometry_rectangle_get_bottom(source_rect);
398 :
399 0 : const double dst_left = geometry_rectangle_get_left(dest_rect);
400 0 : const double dst_right = geometry_rectangle_get_right(dest_rect);
401 0 : const double dst_top = geometry_rectangle_get_top(dest_rect);
402 0 : const double dst_bottom = geometry_rectangle_get_bottom(dest_rect);
403 :
404 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
405 0 : const double gap_dist = 0.499 * object_dist; /* half the object distance allows a line to pass between two objects */
406 :
407 : /* if applicable, add a solution where line is vertical */
408 0 : if (( src_right >= dst_left )&&( src_left <= dst_right ))
409 : {
410 0 : const double min_left = fmax( src_left, dst_left );
411 0 : const double max_right = fmin( src_right, dst_right );
412 :
413 0 : if ( dst_bottom + object_dist < src_top )
414 : {
415 : /* define defaults */
416 0 : double x_value = ( min_left + max_right ) / 2.0;
417 :
418 : /* optimize coordinates */
419 : geometry_rectangle_t search_rect;
420 0 : geometry_rectangle_init_by_corners( &search_rect, min_left, dst_bottom, max_right, src_top );
421 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
422 0 : geometry_rectangle_destroy( &search_rect );
423 :
424 : /* add solution */
425 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
426 : x_value,
427 : src_top,
428 : x_value,
429 : dst_bottom,
430 : x_value
431 : );
432 0 : solutions_count ++;
433 : }
434 0 : else if ( dst_top - object_dist > src_bottom )
435 : {
436 : /* define defaults */
437 0 : double x_value = ( min_left + max_right ) / 2.0;
438 :
439 : /* optimize coordinates */
440 : geometry_rectangle_t search_rect;
441 0 : geometry_rectangle_init_by_corners( &search_rect, min_left, dst_top, max_right, src_bottom );
442 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
443 0 : geometry_rectangle_destroy( &search_rect );
444 :
445 : /* add solution */
446 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
447 : x_value,
448 : src_bottom,
449 : x_value,
450 : dst_top,
451 : x_value
452 : );
453 0 : solutions_count ++;
454 : }
455 : else
456 : {
457 0 : if ( fabs( src_top - dst_top ) > object_dist )
458 : {
459 : /* define defaults */
460 0 : double x_value = ( min_left + max_right ) / 2.0;
461 :
462 : /* optimize coordinates */
463 : geometry_rectangle_t search_rect;
464 0 : geometry_rectangle_init_by_corners( &search_rect, min_left, dst_top, max_right, src_top );
465 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
466 0 : geometry_rectangle_destroy( &search_rect );
467 :
468 : /* add solution */
469 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
470 : x_value,
471 : src_top,
472 : x_value,
473 : dst_top,
474 : x_value
475 : );
476 0 : solutions_count ++;
477 : }
478 :
479 0 : if ( fabs( src_bottom - dst_bottom ) > object_dist )
480 : {
481 : /* define defaults */
482 0 : double x_value = ( min_left + max_right ) / 2.0;
483 :
484 : /* optimize coordinates */
485 : geometry_rectangle_t search_rect;
486 0 : geometry_rectangle_init_by_corners( &search_rect, min_left, src_bottom, max_right, dst_bottom );
487 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
488 0 : geometry_rectangle_destroy( &search_rect );
489 :
490 : /* add solution */
491 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
492 : x_value,
493 : src_bottom,
494 : x_value,
495 : dst_bottom,
496 : x_value
497 : );
498 0 : solutions_count ++;
499 : }
500 : }
501 : }
502 :
503 : /* if applicable, add a solution where line is horizontal */
504 0 : if (( src_bottom >= dst_top )&&( src_top <= dst_bottom ))
505 : {
506 0 : const double min_top = fmax( src_top, dst_top );
507 0 : const double max_bottom = fmin( src_bottom, dst_bottom );
508 :
509 0 : if ( dst_right + object_dist < src_left )
510 : {
511 : /* define defaults */
512 0 : double y_value = ( min_top + max_bottom ) / 2.0;
513 :
514 : /* optimize coordinates */
515 : geometry_rectangle_t search_rect;
516 0 : geometry_rectangle_init_by_corners( &search_rect, dst_right, min_top, src_left, max_bottom );
517 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
518 0 : geometry_rectangle_destroy( &search_rect );
519 :
520 : /* add solution */
521 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
522 : src_left,
523 : y_value,
524 : dst_right,
525 : y_value,
526 : y_value
527 : );
528 0 : solutions_count ++;
529 : }
530 0 : else if ( dst_left - object_dist > src_right )
531 : {
532 : /* define defaults */
533 0 : double y_value = ( min_top + max_bottom ) / 2.0;
534 :
535 : /* optimize coordinates */
536 : geometry_rectangle_t search_rect;
537 0 : geometry_rectangle_init_by_corners( &search_rect, dst_left, min_top, src_right, max_bottom );
538 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
539 0 : geometry_rectangle_destroy( &search_rect );
540 :
541 : /* add solution */
542 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
543 : src_right,
544 : y_value,
545 : dst_left,
546 : y_value,
547 : y_value
548 : );
549 0 : solutions_count ++;
550 : }
551 : else
552 : {
553 0 : if ( fabs( src_left - dst_left ) > object_dist )
554 : {
555 : /* define defaults */
556 0 : double y_value = ( min_top + max_bottom ) / 2.0;
557 :
558 : /* optimize coordinates */
559 : geometry_rectangle_t search_rect;
560 0 : geometry_rectangle_init_by_corners( &search_rect, src_left, min_top, dst_left, max_bottom );
561 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
562 0 : geometry_rectangle_destroy( &search_rect );
563 :
564 : /* add solution */
565 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
566 : src_left,
567 : y_value,
568 : dst_left,
569 : y_value,
570 : y_value
571 : );
572 0 : solutions_count ++;
573 : }
574 :
575 0 : if ( fabs( src_right - dst_right ) > object_dist )
576 : {
577 : /* define defaults */
578 0 : double y_value = ( min_top + max_bottom ) / 2.0;
579 :
580 : /* optimize coordinates */
581 : geometry_rectangle_t search_rect;
582 0 : geometry_rectangle_init_by_corners( &search_rect, src_right, min_top, dst_right, max_bottom );
583 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
584 0 : geometry_rectangle_destroy( &search_rect );
585 :
586 : /* add solution */
587 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
588 : src_right,
589 : y_value,
590 : dst_right,
591 : y_value,
592 : y_value
593 : );
594 0 : solutions_count ++;
595 : }
596 : }
597 : }
598 :
599 0 : *out_solutions_count = solutions_count;
600 :
601 0 : U8_TRACE_END();
602 0 : }
603 :
604 0 : void pencil_relationship_2d_layouter_private_connect_rectangles_by_ZN ( pencil_relationship_2d_layouter_t *this_,
605 : const geometry_rectangle_t *source_rect,
606 : const geometry_rectangle_t *dest_rect,
607 : uint32_t solutions_max,
608 : geometry_connector_t out_solutions[],
609 : uint32_t *out_solutions_count )
610 : {
611 0 : U8_TRACE_BEGIN();
612 0 : assert( NULL != source_rect );
613 0 : assert( NULL != dest_rect );
614 0 : assert ( NULL != out_solutions );
615 0 : assert ( NULL != out_solutions_count );
616 0 : assert ( 2 <= solutions_max ); /* current implementation requires at least 2 options */
617 :
618 0 : uint32_t solutions_count = 0;
619 :
620 0 : const double src_left = geometry_rectangle_get_left(source_rect);
621 0 : const double src_center_x = geometry_rectangle_get_center_x(source_rect);
622 0 : const double src_right = geometry_rectangle_get_right(source_rect);
623 0 : const double src_top = geometry_rectangle_get_top(source_rect);
624 0 : const double src_center_y = geometry_rectangle_get_center_y(source_rect);
625 0 : const double src_bottom = geometry_rectangle_get_bottom(source_rect);
626 0 : const double src_width = geometry_rectangle_get_width(source_rect);
627 0 : const double src_height = geometry_rectangle_get_height(source_rect);
628 :
629 0 : const double dst_left = geometry_rectangle_get_left(dest_rect);
630 0 : const double dst_center_x = geometry_rectangle_get_center_x(dest_rect);
631 0 : const double dst_right = geometry_rectangle_get_right(dest_rect);
632 0 : const double dst_top = geometry_rectangle_get_top(dest_rect);
633 0 : const double dst_center_y = geometry_rectangle_get_center_y(dest_rect);
634 0 : const double dst_bottom = geometry_rectangle_get_bottom(dest_rect);
635 0 : const double dst_width = geometry_rectangle_get_width(dest_rect);
636 0 : const double dst_height = geometry_rectangle_get_height(dest_rect);
637 :
638 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
639 0 : const double good_dist = 2.0 * object_dist; /* duplicate distance: once for each side of the line */
640 0 : const double gap_dist = 0.499 * object_dist; /* half the object distance allows a line to pass between two objects */
641 :
642 : /* if applicable, add a solution where main line is vertical */
643 : {
644 0 : if ( dst_right + good_dist < src_left )
645 : {
646 : /* define defaults */
647 0 : double x_value = ( src_left + dst_right ) / 2.0;
648 0 : double src_y = src_center_y;
649 0 : double dst_y = dst_center_y;
650 :
651 : /* optimize coordinates */
652 : geometry_rectangle_t search_rect;
653 0 : geometry_rectangle_init_by_corners( &search_rect, src_left, src_y, dst_right, dst_y );
654 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
655 0 : geometry_rectangle_destroy( &search_rect );
656 :
657 0 : const geometry_rectangle_t depart_area
658 0 : = { .left=x_value, .top=src_top, .width=(src_left-x_value), .height=src_height};
659 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
660 :
661 0 : const geometry_rectangle_t arrive_area
662 0 : = { .left=dst_right, .top=dst_top, .width=(x_value-dst_right), .height=dst_height};
663 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
664 :
665 : /* add solution */
666 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
667 : src_left,
668 : src_y,
669 : dst_right,
670 : dst_y,
671 : x_value
672 : );
673 0 : solutions_count ++;
674 : }
675 0 : else if ( dst_left - good_dist > src_right )
676 : {
677 : /* define defaults */
678 0 : double x_value = ( src_right + dst_left ) / 2.0;
679 0 : double src_y = src_center_y;
680 0 : double dst_y = dst_center_y;
681 :
682 : /* optimize coordinates */
683 : geometry_rectangle_t search_rect;
684 0 : geometry_rectangle_init_by_corners( &search_rect, src_right, src_y, dst_left, dst_y );
685 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
686 0 : geometry_rectangle_destroy( &search_rect );
687 :
688 0 : const geometry_rectangle_t depart_area
689 0 : = { .left=src_right, .top=src_top, .width=(x_value-src_right), .height=src_height};
690 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
691 :
692 0 : const geometry_rectangle_t arrive_area
693 0 : = { .left=x_value, .top=dst_top, .width=(dst_left-x_value), .height=dst_height};
694 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
695 :
696 : /* add solution */
697 0 : geometry_connector_reinit_vertical ( &(out_solutions[solutions_count]),
698 : src_right,
699 : src_y,
700 : dst_left,
701 : dst_y,
702 : x_value
703 : );
704 0 : solutions_count ++;
705 : }
706 : }
707 :
708 : /* if applicable, add a solution where main line is horizontal */
709 : {
710 0 : if ( dst_bottom + good_dist < src_top )
711 : {
712 : /* define defaults */
713 0 : double y_value = ( src_top + dst_bottom ) / 2.0;
714 0 : double src_x = src_center_x;
715 0 : double dst_x = dst_center_x;
716 :
717 : /* optimize coordinates */
718 : geometry_rectangle_t search_rect;
719 0 : geometry_rectangle_init_by_corners( &search_rect, src_x, src_top, dst_x, dst_bottom );
720 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
721 0 : geometry_rectangle_destroy( &search_rect );
722 :
723 0 : const geometry_rectangle_t depart_area
724 0 : = { .left=src_left, .top=y_value, .width=src_width, .height=(src_top-y_value)};
725 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
726 :
727 0 : const geometry_rectangle_t arrive_area
728 0 : = { .left=dst_left, .top=dst_bottom, .width=dst_width, .height=(y_value-dst_bottom)};
729 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
730 :
731 : /* add solution */
732 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
733 : src_x,
734 : src_top,
735 : dst_x,
736 : dst_bottom,
737 : y_value
738 : );
739 0 : solutions_count ++;
740 : }
741 0 : else if ( dst_top - good_dist > src_bottom )
742 : {
743 : /* define defaults */
744 0 : double y_value = ( src_bottom + dst_top ) / 2.0;
745 0 : double src_x = src_center_x;
746 0 : double dst_x = dst_center_x;
747 :
748 : /* optimize coordinates */
749 : geometry_rectangle_t search_rect;
750 0 : geometry_rectangle_init_by_corners( &search_rect, src_x, src_bottom, dst_x, dst_top );
751 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
752 0 : geometry_rectangle_destroy( &search_rect );
753 :
754 0 : const geometry_rectangle_t depart_area
755 0 : = { .left=src_left, .top=src_bottom, .width=src_width, .height=(y_value-src_bottom)};
756 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
757 :
758 0 : const geometry_rectangle_t arrive_area
759 0 : = { .left=dst_left, .top=y_value, .width=dst_width, .height=(dst_top-y_value)};
760 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
761 :
762 : /* add solution */
763 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
764 : src_x,
765 : src_bottom,
766 : dst_x,
767 : dst_top,
768 : y_value
769 : );
770 0 : solutions_count ++;
771 : }
772 : }
773 :
774 0 : *out_solutions_count = solutions_count;
775 :
776 0 : U8_TRACE_END();
777 0 : }
778 :
779 0 : void pencil_relationship_2d_layouter_private_connect_rectangles_by_UC ( pencil_relationship_2d_layouter_t *this_,
780 : const geometry_rectangle_t *source_rect,
781 : const geometry_rectangle_t *dest_rect,
782 : uint32_t solutions_max,
783 : geometry_connector_t out_solutions[],
784 : uint32_t *out_solutions_count )
785 : {
786 0 : U8_TRACE_BEGIN();
787 0 : assert( NULL != source_rect );
788 0 : assert( NULL != dest_rect );
789 0 : assert ( NULL != out_solutions );
790 0 : assert ( NULL != out_solutions_count );
791 0 : assert ( 4 <= solutions_max ); /* current implementation calculates exactly 4 options */
792 :
793 0 : uint32_t solutions_count = 0;
794 :
795 : /* get draw area */
796 : const layout_diagram_t *const diagram_layout
797 0 : = layout_visible_set_get_diagram_ptr( (*this_).layout_data );
798 : const geometry_rectangle_t *const diagram_draw_area
799 0 : = layout_diagram_get_draw_area_const( diagram_layout );
800 0 : const double draw_left = geometry_rectangle_get_left( diagram_draw_area );
801 0 : const double draw_right = geometry_rectangle_get_right( diagram_draw_area );
802 0 : const double draw_top = geometry_rectangle_get_top( diagram_draw_area );
803 0 : const double draw_bottom = geometry_rectangle_get_bottom( diagram_draw_area );
804 :
805 0 : const double src_left = geometry_rectangle_get_left(source_rect);
806 0 : const double src_center_x = geometry_rectangle_get_center_x(source_rect);
807 0 : const double src_right = geometry_rectangle_get_right(source_rect);
808 0 : const double src_top = geometry_rectangle_get_top(source_rect);
809 0 : const double src_center_y = geometry_rectangle_get_center_y(source_rect);
810 0 : const double src_bottom = geometry_rectangle_get_bottom(source_rect);
811 0 : const double src_width = geometry_rectangle_get_width(source_rect);
812 0 : const double src_height = geometry_rectangle_get_height(source_rect);
813 :
814 0 : const double dst_left = geometry_rectangle_get_left(dest_rect);
815 0 : const double dst_center_x = geometry_rectangle_get_center_x(dest_rect);
816 0 : const double dst_right = geometry_rectangle_get_right(dest_rect);
817 0 : const double dst_top = geometry_rectangle_get_top(dest_rect);
818 0 : const double dst_center_y = geometry_rectangle_get_center_y(dest_rect);
819 0 : const double dst_bottom = geometry_rectangle_get_bottom(dest_rect);
820 0 : const double dst_width = geometry_rectangle_get_width(dest_rect);
821 0 : const double dst_height = geometry_rectangle_get_height(dest_rect);
822 :
823 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
824 0 : const double gap_dist = 0.499 * object_dist; /* half the object distance allows a line to pass between two objects */
825 : static const double NO_TOUCH = 0.0001;
826 :
827 : /* connect via left side */
828 : {
829 : /* define defaults */
830 0 : double x_value = fmin( src_left, dst_left ) - object_dist;
831 0 : double src_y = src_center_y;
832 0 : double dst_y = dst_center_y;
833 0 : if ( fabs( src_center_y - dst_center_y ) < NO_TOUCH )
834 : {
835 : /* forward way is identical to retour - may be a relation to self */
836 0 : src_y = fmin( src_center_y + gap_dist, src_bottom );
837 0 : dst_y = fmax( dst_center_y - gap_dist, dst_top );
838 : }
839 :
840 : /* optimize coordinates */
841 : geometry_rectangle_t search_rect;
842 0 : geometry_rectangle_init_by_corners( &search_rect, draw_left, src_y, x_value, dst_y );
843 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
844 0 : geometry_rectangle_destroy( &search_rect );
845 :
846 0 : const geometry_rectangle_t depart_area
847 0 : = { .left=x_value, .top=src_top, .width=(src_left-x_value), .height=src_height};
848 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
849 :
850 0 : const geometry_rectangle_t arrive_area
851 0 : = { .left=x_value, .top=dst_top, .width=(dst_left-x_value), .height=dst_height};
852 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
853 :
854 : /* add solution */
855 0 : geometry_connector_reinit_vertical( &(out_solutions[solutions_count]),
856 : src_left,
857 : src_y,
858 : dst_left,
859 : dst_y,
860 : x_value
861 : );
862 0 : solutions_count ++;
863 : }
864 :
865 : /* connect via right side */
866 : {
867 : /* define defaults */
868 0 : double x_value = fmax( src_right, dst_right ) + object_dist;
869 0 : double src_y = src_center_y;
870 0 : double dst_y = dst_center_y;
871 0 : if ( fabs( src_center_y - dst_center_y ) < NO_TOUCH )
872 : {
873 : /* forward way is identical to retour - may be a relation to self */
874 0 : src_y = fmin( src_center_y + gap_dist, src_bottom );
875 0 : dst_y = fmax( dst_center_y - gap_dist, dst_top );
876 : }
877 :
878 : /* optimize coordinates */
879 : geometry_rectangle_t search_rect;
880 0 : geometry_rectangle_init_by_corners( &search_rect, x_value, src_y, draw_right, dst_y );
881 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &search_rect, gap_dist, &x_value );
882 0 : geometry_rectangle_destroy( &search_rect );
883 :
884 0 : const geometry_rectangle_t depart_area
885 0 : = { .left=src_right, .top=src_top, .width=(x_value-src_right), .height=src_height};
886 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
887 :
888 0 : const geometry_rectangle_t arrive_area
889 0 : = { .left=dst_right, .top=dst_top, .width=(x_value-dst_right), .height=dst_height};
890 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
891 :
892 : /* add solution */
893 0 : geometry_connector_reinit_vertical( &(out_solutions[solutions_count]),
894 : src_right,
895 : src_y,
896 : dst_right,
897 : dst_y,
898 : x_value
899 : );
900 0 : solutions_count ++;
901 : }
902 :
903 : /* connect via top side */
904 : {
905 : /* define defaults */
906 0 : double y_value = fmin( src_top, dst_top ) - object_dist;
907 0 : double src_x = src_center_x;
908 0 : double dst_x = dst_center_x;
909 0 : if ( fabs( src_center_x - dst_center_x ) < NO_TOUCH )
910 : {
911 : /* forward way is identical to retour - may be a relation to self */
912 0 : src_x = fmax( src_center_x - gap_dist, src_left );
913 0 : dst_x = fmin( dst_center_x + gap_dist, dst_right );
914 : }
915 :
916 : /* optimize coordinates */
917 : geometry_rectangle_t search_rect;
918 0 : geometry_rectangle_init_by_corners( &search_rect, src_x, draw_top, dst_x, y_value );
919 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
920 0 : geometry_rectangle_destroy( &search_rect );
921 :
922 0 : const geometry_rectangle_t depart_area
923 0 : = { .left=src_left, .top=y_value, .width=src_width, .height=(src_top-y_value)};
924 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
925 :
926 0 : const geometry_rectangle_t arrive_area
927 0 : = { .left=dst_left, .top=y_value, .width=dst_width, .height=(dst_top-y_value)};
928 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
929 :
930 : /* add solution */
931 0 : geometry_connector_reinit_horizontal( &(out_solutions[solutions_count]),
932 : src_x,
933 : src_top,
934 : dst_x,
935 : dst_top,
936 : y_value
937 : );
938 0 : solutions_count ++;
939 : }
940 :
941 : /* connect via bottom side */
942 : {
943 : /* define defaults */
944 0 : double y_value = fmax( src_bottom, dst_bottom ) + object_dist;
945 0 : double src_x = src_center_x;
946 0 : double dst_x = dst_center_x;
947 0 : if ( fabs( src_center_x - dst_center_x ) < NO_TOUCH )
948 : {
949 : /* forward way is identical to retour - may be a relation to self */
950 0 : src_x = fmax( src_center_x - gap_dist, src_left );
951 0 : dst_x = fmin( dst_center_x + gap_dist, dst_right );
952 : }
953 :
954 : /* optimize coordinates */
955 : geometry_rectangle_t search_rect;
956 0 : geometry_rectangle_init_by_corners( &search_rect, src_x, y_value, dst_x, draw_bottom );
957 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &search_rect, gap_dist, &y_value );
958 0 : geometry_rectangle_destroy( &search_rect );
959 :
960 0 : const geometry_rectangle_t depart_area
961 0 : = { .left=src_left, .top=src_bottom, .width=src_width, .height=(y_value-src_bottom)};
962 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
963 :
964 0 : const geometry_rectangle_t arrive_area
965 0 : = { .left=dst_left, .top=dst_bottom, .width=dst_width, .height=(y_value-dst_bottom)};
966 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
967 :
968 : /* add solution */
969 0 : geometry_connector_reinit_horizontal( &(out_solutions[solutions_count]),
970 : src_x,
971 : src_bottom,
972 : dst_x,
973 : dst_bottom,
974 : y_value
975 : );
976 0 : solutions_count ++;
977 : }
978 :
979 0 : *out_solutions_count = solutions_count;
980 :
981 0 : U8_TRACE_END();
982 0 : }
983 :
984 0 : void pencil_relationship_2d_layouter_private_connect_rectangles_by_L7 ( pencil_relationship_2d_layouter_t *this_,
985 : const geometry_rectangle_t *source_rect,
986 : const geometry_rectangle_t *dest_rect,
987 : uint32_t solutions_max,
988 : geometry_connector_t out_solutions[],
989 : uint32_t *out_solutions_count )
990 : {
991 0 : U8_TRACE_BEGIN();
992 0 : assert( NULL != source_rect );
993 0 : assert( NULL != dest_rect );
994 0 : assert ( NULL != out_solutions );
995 0 : assert ( NULL != out_solutions_count );
996 0 : assert ( 8 <= solutions_max ); /* current implementation requires at least 2 options */
997 :
998 0 : uint32_t solutions_count = 0;
999 :
1000 0 : const double src_left = geometry_rectangle_get_left(source_rect);
1001 0 : const double src_right = geometry_rectangle_get_right(source_rect);
1002 0 : const double src_top = geometry_rectangle_get_top(source_rect);
1003 0 : const double src_bottom = geometry_rectangle_get_bottom(source_rect);
1004 :
1005 0 : const double dst_left = geometry_rectangle_get_left(dest_rect);
1006 0 : const double dst_right = geometry_rectangle_get_right(dest_rect);
1007 0 : const double dst_top = geometry_rectangle_get_top(dest_rect);
1008 0 : const double dst_bottom = geometry_rectangle_get_bottom(dest_rect);
1009 :
1010 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
1011 0 : const double gap_dist = 0.499 * object_dist; /* half the object distance allows a line to pass between two objects */
1012 :
1013 : /* pre-calculate some intermediate values on source rect */
1014 0 : const bool src_left_to_outside = dst_left < src_left; /* connector starts towards outside of source rect */
1015 0 : const bool src_right_to_outside = dst_right > src_right; /* connector starts towards outside of source rect */
1016 0 : const bool src_top_to_outside = src_top > dst_top; /* connector arrives from outside at dest rect */
1017 0 : const bool src_bottom_to_outside = src_bottom < dst_bottom; /* connector arrives from outside at dest rect */
1018 : /* pre-calculate some intermediate values on destination rect */
1019 0 : const bool dst_left_from_outside = src_left < dst_left; /* connector starts towards outside of source rect */
1020 0 : const bool dst_right_from_outside = src_right > dst_right; /* connector starts towards outside of source rect */
1021 0 : const bool dst_top_from_outside = dst_top > src_top; /* connector arrives from outside at dest rect */
1022 0 : const bool dst_bottom_from_outside = dst_bottom < src_bottom; /* connector arrives from outside at dest rect */
1023 :
1024 : /* add two solutions from source-left */
1025 : {
1026 0 : const double search_left = dst_left;
1027 0 : const double search_right = src_left_to_outside ? fmin( src_left, dst_right ) : dst_right;
1028 : /* add a solution from source-left to destination-bottom */
1029 : {
1030 0 : const double search_top = dst_bottom_from_outside ? fmax( src_top, dst_bottom ) : src_top;
1031 0 : const double search_bottom = src_bottom;
1032 :
1033 : /* define defaults */
1034 0 : double dst_x = ( search_left + search_right ) / 2.0;
1035 0 : double src_y = ( search_top + search_bottom ) / 2.0;
1036 :
1037 : /* optimize coordinates */
1038 : geometry_rectangle_t depart_area;
1039 0 : geometry_rectangle_init_by_corners( &depart_area, dst_x, src_top, src_left, src_bottom );
1040 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
1041 0 : geometry_rectangle_destroy( &depart_area );
1042 :
1043 : geometry_rectangle_t arrive_area;
1044 0 : geometry_rectangle_init_by_corners( &arrive_area, dst_left, src_y, dst_right, dst_bottom );
1045 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
1046 0 : geometry_rectangle_destroy( &arrive_area );
1047 :
1048 : /* add solution */
1049 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1050 : src_left,
1051 : src_y,
1052 : dst_x,
1053 : dst_bottom,
1054 : src_y
1055 : );
1056 0 : solutions_count ++;
1057 : }
1058 : /* add a solution from source-left to destination-top */
1059 : {
1060 0 : const double search_top = src_top;
1061 0 : const double search_bottom = dst_top_from_outside ? fmin( src_bottom, dst_top ) : src_bottom;
1062 :
1063 : /* define defaults */
1064 0 : double dst_x = ( search_left + search_right ) / 2.0;
1065 0 : double src_y = ( search_top + search_bottom ) / 2.0;
1066 :
1067 : /* optimize coordinates */
1068 : geometry_rectangle_t depart_area;
1069 0 : geometry_rectangle_init_by_corners( &depart_area, dst_x, src_top, src_left, src_bottom );
1070 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
1071 0 : geometry_rectangle_destroy( &depart_area );
1072 :
1073 : geometry_rectangle_t arrive_area;
1074 0 : geometry_rectangle_init_by_corners( &arrive_area, dst_left, src_y, dst_right, dst_top );
1075 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
1076 0 : geometry_rectangle_destroy( &arrive_area );
1077 :
1078 : /* add solution */
1079 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1080 : src_left,
1081 : src_y,
1082 : dst_x,
1083 : dst_top,
1084 : src_y
1085 : );
1086 0 : solutions_count ++;
1087 : }
1088 : }
1089 :
1090 : /* add two solutions from source-right */
1091 : {
1092 0 : const double search_left = src_right_to_outside ? fmax( src_right, dst_left ) : dst_left;
1093 0 : const double search_right = dst_right;
1094 : /* add a solution from source-right to destination-bottom */
1095 : {
1096 0 : const double search_top = dst_bottom_from_outside ? fmax( src_top, dst_bottom ) : src_top;
1097 0 : const double search_bottom = src_bottom;
1098 :
1099 : /* define defaults */
1100 0 : double dst_x = ( search_left + search_right ) / 2.0;
1101 0 : double src_y = ( search_top + search_bottom ) / 2.0;
1102 :
1103 : /* optimize coordinates */
1104 : geometry_rectangle_t depart_area;
1105 0 : geometry_rectangle_init_by_corners( &depart_area, dst_x, src_top, src_right, src_bottom );
1106 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
1107 0 : geometry_rectangle_destroy( &depart_area );
1108 :
1109 : geometry_rectangle_t arrive_area;
1110 0 : geometry_rectangle_init_by_corners( &arrive_area, dst_left, src_y, dst_right, dst_bottom );
1111 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
1112 0 : geometry_rectangle_destroy( &arrive_area );
1113 :
1114 : /* add solution */
1115 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1116 : src_right,
1117 : src_y,
1118 : dst_x,
1119 : dst_bottom,
1120 : src_y
1121 : );
1122 0 : solutions_count ++;
1123 : }
1124 : /* add a solution from source-right to destination-top */
1125 : {
1126 0 : const double search_top = src_top;
1127 0 : const double search_bottom = dst_top_from_outside ? fmin( src_bottom, dst_top ) : src_bottom;
1128 :
1129 : /* define defaults */
1130 0 : double dst_x = ( search_left + search_right ) / 2.0;
1131 0 : double src_y = ( search_top + search_bottom ) / 2.0;
1132 :
1133 : /* optimize coordinates */
1134 : geometry_rectangle_t depart_area;
1135 0 : geometry_rectangle_init_by_corners( &depart_area, dst_x, src_top, src_right, src_bottom );
1136 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &depart_area, gap_dist, &src_y );
1137 0 : geometry_rectangle_destroy( &depart_area );
1138 :
1139 : geometry_rectangle_t arrive_area;
1140 0 : geometry_rectangle_init_by_corners( &arrive_area, dst_left, src_y, dst_right, dst_top );
1141 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &arrive_area, gap_dist, &dst_x );
1142 0 : geometry_rectangle_destroy( &arrive_area );
1143 :
1144 : /* add solution */
1145 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1146 : src_right,
1147 : src_y,
1148 : dst_x,
1149 : dst_top,
1150 : src_y
1151 : );
1152 0 : solutions_count ++;
1153 : }
1154 : }
1155 :
1156 : /* add two solutions from source-top */
1157 : {
1158 0 : const double search_top = dst_top;
1159 0 : const double search_bottom = src_top_to_outside ? fmin( src_top, dst_bottom ) : dst_bottom;
1160 : /* add a solution from source-top to destination-right */
1161 : {
1162 0 : const double search_left = dst_right_from_outside ? fmax( dst_right, src_left ) : src_left;
1163 0 : const double search_right = src_right;
1164 :
1165 : /* define defaults */
1166 0 : double src_x = ( search_left + search_right ) / 2.0;
1167 0 : double dst_y = ( search_top + search_bottom ) / 2.0;
1168 :
1169 : /* optimize coordinates */
1170 : geometry_rectangle_t depart_area;
1171 0 : geometry_rectangle_init_by_corners( &depart_area, src_left, src_top, src_right, dst_y );
1172 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
1173 0 : geometry_rectangle_destroy( &depart_area );
1174 :
1175 : geometry_rectangle_t arrive_area;
1176 0 : geometry_rectangle_init_by_corners( &arrive_area, src_x, dst_top, dst_right, dst_bottom );
1177 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
1178 0 : geometry_rectangle_destroy( &arrive_area );
1179 :
1180 : /* add solution */
1181 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1182 : src_x,
1183 : src_top,
1184 : dst_right,
1185 : dst_y,
1186 : dst_y
1187 : );
1188 0 : solutions_count ++;
1189 : }
1190 : /* add a solution from source-top to destination-left */
1191 : {
1192 0 : const double search_left = src_left;
1193 0 : const double search_right = dst_left_from_outside ? fmin( dst_left, src_right ) : src_right;
1194 :
1195 : /* define defaults */
1196 0 : double src_x = ( search_left + search_right ) / 2.0;
1197 0 : double dst_y = ( search_top + search_bottom ) / 2.0;
1198 :
1199 : /* optimize coordinates */
1200 : geometry_rectangle_t depart_area;
1201 0 : geometry_rectangle_init_by_corners( &depart_area, src_left, src_top, src_right, dst_y );
1202 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
1203 0 : geometry_rectangle_destroy( &depart_area );
1204 :
1205 : geometry_rectangle_t arrive_area;
1206 0 : geometry_rectangle_init_by_corners( &arrive_area, src_x, dst_top, dst_left, dst_bottom );
1207 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
1208 0 : geometry_rectangle_destroy( &arrive_area );
1209 :
1210 : /* add solution */
1211 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1212 : src_x,
1213 : src_top,
1214 : dst_left,
1215 : dst_y,
1216 : dst_y
1217 : );
1218 0 : solutions_count ++;
1219 : }
1220 : }
1221 :
1222 : /* add two solutions from source-bottom */
1223 : {
1224 0 : const double search_top = src_bottom_to_outside ? fmax( src_bottom, dst_top ) : dst_top;
1225 0 : const double search_bottom = dst_bottom;
1226 : /* add a solution from source-bottom to destination-right */
1227 : {
1228 0 : const double search_left = dst_right_from_outside ? fmax( dst_right, src_left ) : src_left;
1229 0 : const double search_right = src_right;
1230 :
1231 : /* define defaults */
1232 0 : double src_x = ( search_left + search_right ) / 2.0;
1233 0 : double dst_y = ( search_top + search_bottom ) / 2.0;
1234 :
1235 : /* optimize coordinates */
1236 : geometry_rectangle_t depart_area;
1237 0 : geometry_rectangle_init_by_corners( &depart_area, src_left, src_bottom, src_right, dst_y );
1238 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
1239 0 : geometry_rectangle_destroy( &depart_area );
1240 :
1241 : geometry_rectangle_t arrive_area;
1242 0 : geometry_rectangle_init_by_corners( &arrive_area, src_x, dst_top, dst_right, dst_bottom );
1243 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
1244 0 : geometry_rectangle_destroy( &arrive_area );
1245 :
1246 : /* add solution */
1247 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1248 : src_x,
1249 : src_bottom,
1250 : dst_right,
1251 : dst_y,
1252 : dst_y
1253 : );
1254 0 : solutions_count ++;
1255 : }
1256 : /* add a solution from source-bottom to destination-left */
1257 : {
1258 0 : const double search_left = src_left;
1259 0 : const double search_right = dst_left_from_outside ? fmin( dst_left, src_right ) : src_right;
1260 :
1261 : /* define defaults */
1262 0 : double src_x = ( search_left + search_right ) / 2.0;
1263 0 : double dst_y = ( search_top + search_bottom ) / 2.0;
1264 :
1265 : /* optimize coordinates */
1266 : geometry_rectangle_t depart_area;
1267 0 : geometry_rectangle_init_by_corners( &depart_area, src_left, src_bottom, src_right, dst_y );
1268 0 : pencil_relationship_2d_layouter_private_find_space_for_v_line ( this_, &depart_area, gap_dist, &src_x );
1269 0 : geometry_rectangle_destroy( &depart_area );
1270 :
1271 : geometry_rectangle_t arrive_area;
1272 0 : geometry_rectangle_init_by_corners( &arrive_area, src_x, dst_top, dst_left, dst_bottom );
1273 0 : pencil_relationship_2d_layouter_private_find_space_for_h_line ( this_, &arrive_area, gap_dist, &dst_y );
1274 0 : geometry_rectangle_destroy( &arrive_area );
1275 :
1276 : /* add solution */
1277 0 : geometry_connector_reinit_horizontal ( &(out_solutions[solutions_count]),
1278 : src_x,
1279 : src_bottom,
1280 : dst_left,
1281 : dst_y,
1282 : dst_y
1283 : );
1284 0 : solutions_count ++;
1285 : }
1286 : }
1287 :
1288 0 : *out_solutions_count = solutions_count;
1289 :
1290 0 : U8_TRACE_END();
1291 0 : }
1292 :
1293 0 : u8_error_t pencil_relationship_2d_layouter_private_find_space_for_line ( pencil_relationship_2d_layouter_t *this_,
1294 : const geometry_rectangle_t *search_rect,
1295 : bool horizontal_line,
1296 : double min_gap,
1297 : double *io_coordinate )
1298 : {
1299 0 : U8_TRACE_BEGIN();
1300 0 : assert ( NULL != search_rect );
1301 0 : assert ( NULL != io_coordinate );
1302 0 : u8_error_t err = U8_ERROR_NONE;
1303 :
1304 : /* start two probes at the center and move these to the boundaries when discovering overlaps */
1305 0 : const double center = *io_coordinate;
1306 0 : if ( horizontal_line )
1307 : {
1308 0 : assert( center > geometry_rectangle_get_top( search_rect ) - 0.000000001 );
1309 0 : assert( center < geometry_rectangle_get_bottom( search_rect ) + 0.000000001 );
1310 : }
1311 : else
1312 : {
1313 0 : assert( center > geometry_rectangle_get_left( search_rect ) - 0.000000001 );
1314 0 : assert( center < geometry_rectangle_get_right( search_rect ) + 0.000000001 );
1315 : }
1316 0 : double good_smaller = center; /* a coordinate top/left of major obstacles */
1317 0 : double good_greater = center; /* a coordinate bottom/right of major obstacles */
1318 0 : double best_smaller = center; /* a coordinate top/left of any obstacle */
1319 0 : double best_greater = center; /* a coordinate bottom/right of any obstacle */
1320 :
1321 : /* the rectangle where each classifier within is checked for intersections: */
1322 : geometry_rectangle_t consider_rect;
1323 0 : geometry_rectangle_copy( &consider_rect, search_rect );
1324 0 : if ( horizontal_line )
1325 : {
1326 0 : geometry_rectangle_set_top( &consider_rect, geometry_rectangle_get_top( search_rect ) - min_gap );
1327 0 : geometry_rectangle_set_height( &consider_rect, geometry_rectangle_get_height( search_rect ) + 2.0 * min_gap );
1328 :
1329 : }
1330 : else
1331 : {
1332 0 : geometry_rectangle_set_left( &consider_rect, geometry_rectangle_get_left( search_rect ) - min_gap );
1333 0 : geometry_rectangle_set_width( &consider_rect, geometry_rectangle_get_width( search_rect ) + 2.0 * min_gap );
1334 : }
1335 0 : const double minimum_result
1336 : = horizontal_line
1337 0 : ? geometry_rectangle_get_top( search_rect )
1338 0 : : geometry_rectangle_get_left( search_rect );
1339 0 : const double maximum_result
1340 : = horizontal_line
1341 0 : ? geometry_rectangle_get_bottom( search_rect )
1342 0 : : geometry_rectangle_get_right( search_rect );
1343 :
1344 : /* iterate till no hit anymore */
1345 0 : const uint32_t max_list_iteration = 8; /* in any case, do not iterate ofer the list more than 8 times */
1346 0 : bool hit = true; /* whenever the probes hit a rectangle, hit is set to true */
1347 0 : for ( uint32_t list_iteration = 0; (list_iteration < max_list_iteration) && hit; list_iteration ++ )
1348 : {
1349 0 : hit = false;
1350 :
1351 : /* move away from classifiers */
1352 0 : const uint32_t count_classifiers = layout_visible_set_get_visible_classifier_count ( (*this_).layout_data );
1353 0 : for ( uint32_t classifier_index = 0; classifier_index < count_classifiers; classifier_index ++ )
1354 : {
1355 : const layout_visible_classifier_t *const the_classifier
1356 0 : = layout_visible_set_get_visible_classifier_ptr( (*this_).layout_data, classifier_index );
1357 :
1358 : const geometry_rectangle_t *const classifier_symbol_box
1359 0 : = layout_visible_classifier_get_symbol_box_const( the_classifier );
1360 : const geometry_rectangle_t *const classifier_space
1361 0 : = layout_visible_classifier_get_space_const( the_classifier );
1362 : /* Note: This algorithm ignores if the current classifier is parent container of source or destination */
1363 0 : if ( geometry_rectangle_is_intersecting( &consider_rect, classifier_symbol_box ) )
1364 : {
1365 0 : const double clas_symbol_box_smaller
1366 : = horizontal_line /* do vertical search if line is horizontal */
1367 0 : ? ( geometry_rectangle_get_top(classifier_symbol_box) - min_gap )
1368 0 : : ( geometry_rectangle_get_left(classifier_symbol_box) - min_gap );
1369 0 : const double clas_symbol_box_greater
1370 : = horizontal_line /* do vertical search if line is horizontal */
1371 0 : ? ( geometry_rectangle_get_bottom(classifier_symbol_box) + min_gap )
1372 0 : : ( geometry_rectangle_get_right(classifier_symbol_box) + min_gap );
1373 :
1374 0 : const double undo_good_smaller = good_smaller;
1375 0 : const double undo_good_greater = good_greater;
1376 0 : const bool undo_hit = hit;
1377 0 : const geometry_rectangle_t good_smaller_rect
1378 0 : = { .left = horizontal_line ? geometry_rectangle_get_left( search_rect ) : good_smaller,
1379 0 : .top = horizontal_line ? good_smaller : geometry_rectangle_get_top( search_rect ),
1380 0 : .width = horizontal_line ? geometry_rectangle_get_width( search_rect ) : 0.0,
1381 0 : .height = horizontal_line ? 0.0 : geometry_rectangle_get_height( search_rect )
1382 : };
1383 0 : if ( ( ! geometry_rectangle_is_containing( classifier_space, &good_smaller_rect ) )
1384 0 : && ( clas_symbol_box_smaller < good_smaller ) && ( good_smaller < clas_symbol_box_greater ) )
1385 : {
1386 0 : good_smaller = clas_symbol_box_smaller;
1387 0 : hit = true;
1388 : }
1389 0 : const geometry_rectangle_t good_greater_rect
1390 0 : = { .left = horizontal_line ? geometry_rectangle_get_left( search_rect ) : good_greater,
1391 0 : .top = horizontal_line ? good_greater : geometry_rectangle_get_top( search_rect ),
1392 0 : .width = horizontal_line ? geometry_rectangle_get_width( search_rect ) : 0.0,
1393 0 : .height = horizontal_line ? 0.0 : geometry_rectangle_get_height( search_rect )
1394 : };
1395 0 : if ( ( ! geometry_rectangle_is_containing( classifier_space, &good_greater_rect ) )
1396 0 : && ( clas_symbol_box_smaller < good_greater ) && ( good_greater < clas_symbol_box_greater ) )
1397 : {
1398 0 : good_greater = clas_symbol_box_greater;
1399 0 : hit = true;
1400 : }
1401 0 : const bool no_solution_remaining
1402 0 : = ( good_smaller < minimum_result )&&( good_greater > maximum_result );
1403 0 : if ( no_solution_remaining )
1404 : {
1405 : /* restore old values */
1406 0 : good_smaller = undo_good_smaller;
1407 0 : good_greater = undo_good_greater;
1408 0 : hit = undo_hit;
1409 : }
1410 0 : const geometry_rectangle_t best_smaller_rect
1411 0 : = { .left = horizontal_line ? geometry_rectangle_get_left( search_rect ) : best_smaller,
1412 0 : .top = horizontal_line ? best_smaller : geometry_rectangle_get_top( search_rect ),
1413 0 : .width = horizontal_line ? geometry_rectangle_get_width( search_rect ) : 0.0,
1414 0 : .height = horizontal_line ? 0.0 : geometry_rectangle_get_height( search_rect )
1415 : };
1416 0 : if ( ( ! geometry_rectangle_is_containing( classifier_space, &best_smaller_rect ) )
1417 0 : && ( clas_symbol_box_smaller < best_smaller ) && ( best_smaller < clas_symbol_box_greater ) )
1418 : {
1419 0 : best_smaller = clas_symbol_box_smaller;
1420 0 : hit = true;
1421 : }
1422 0 : const geometry_rectangle_t best_greater_rect
1423 0 : = { .left = horizontal_line ? geometry_rectangle_get_left( search_rect ) : best_greater,
1424 0 : .top = horizontal_line ? best_greater : geometry_rectangle_get_top( search_rect ),
1425 0 : .width = horizontal_line ? geometry_rectangle_get_width( search_rect ) : 0.0,
1426 0 : .height = horizontal_line ? 0.0 : geometry_rectangle_get_height( search_rect )
1427 : };
1428 0 : if ( ( ! geometry_rectangle_is_containing( classifier_space, &best_greater_rect ) )
1429 0 : && ( clas_symbol_box_smaller < best_greater ) && ( best_greater < clas_symbol_box_greater ) )
1430 : {
1431 0 : best_greater = clas_symbol_box_greater;
1432 0 : hit = true;
1433 : }
1434 : }
1435 :
1436 : const geometry_rectangle_t *const classifier_label_box
1437 0 : = layout_visible_classifier_get_label_box_const( the_classifier );
1438 0 : if ( geometry_rectangle_is_intersecting( &consider_rect, classifier_label_box ) )
1439 : {
1440 0 : const double clas_label_smaller
1441 : = horizontal_line /* do vertical search if line is horizontal */
1442 0 : ? ( geometry_rectangle_get_top(classifier_label_box) - min_gap )
1443 0 : : ( geometry_rectangle_get_left(classifier_label_box) - min_gap );
1444 0 : const double clas_label_greater
1445 : = horizontal_line /* do vertical search if line is horizontal */
1446 0 : ? ( geometry_rectangle_get_bottom(classifier_label_box) + min_gap )
1447 0 : : ( geometry_rectangle_get_right(classifier_label_box) + min_gap );
1448 :
1449 0 : if ( ( clas_label_smaller < good_smaller ) && ( good_smaller < clas_label_greater ) )
1450 : {
1451 0 : good_smaller = clas_label_smaller;
1452 0 : hit = true;
1453 : }
1454 0 : if ( ( clas_label_smaller < good_greater ) && ( good_greater < clas_label_greater ) )
1455 : {
1456 0 : good_greater = clas_label_greater;
1457 0 : hit = true;
1458 : }
1459 0 : if ( ( clas_label_smaller < best_smaller ) && ( best_smaller < clas_label_greater ) )
1460 : {
1461 0 : best_smaller = clas_label_smaller;
1462 0 : hit = true;
1463 : }
1464 0 : if ( ( clas_label_smaller < best_greater ) && ( best_greater < clas_label_greater ) )
1465 : {
1466 0 : best_greater = clas_label_greater;
1467 0 : hit = true;
1468 : }
1469 : }
1470 : }
1471 :
1472 : /* move away from features, check symbol boxes only, label boxes are not yet initialized */
1473 0 : const uint32_t count_features = layout_visible_set_get_feature_count ( (*this_).layout_data );
1474 0 : for ( uint32_t f_idx = 0; f_idx < count_features; f_idx ++ )
1475 : {
1476 : const layout_feature_t *const feature_layout
1477 0 : = layout_visible_set_get_feature_ptr ( (*this_).layout_data, f_idx );
1478 :
1479 : const geometry_rectangle_t *const feature_symbol_box
1480 0 : = layout_feature_get_symbol_box_const( feature_layout );
1481 0 : if ( geometry_rectangle_is_intersecting( &consider_rect, feature_symbol_box ) )
1482 : {
1483 0 : const double feature_smaller
1484 : = horizontal_line /* do vertical search if line is horizontal */
1485 0 : ? ( geometry_rectangle_get_top(feature_symbol_box) - min_gap )
1486 0 : : ( geometry_rectangle_get_left(feature_symbol_box) - min_gap );
1487 0 : const double feature_greater
1488 : = horizontal_line /* do vertical search if line is horizontal */
1489 0 : ? ( geometry_rectangle_get_bottom(feature_symbol_box) + min_gap )
1490 0 : : ( geometry_rectangle_get_right(feature_symbol_box) + min_gap );
1491 :
1492 0 : if ( ( feature_smaller < good_smaller ) && ( good_smaller < feature_greater ) )
1493 : {
1494 0 : good_smaller = feature_smaller;
1495 0 : hit = true;
1496 : }
1497 0 : if ( ( feature_smaller < good_greater ) && ( good_greater < feature_greater ) )
1498 : {
1499 0 : good_greater = feature_greater;
1500 0 : hit = true;
1501 : }
1502 0 : if ( ( feature_smaller < best_smaller ) && ( best_smaller < feature_greater ) )
1503 : {
1504 0 : best_smaller = feature_smaller;
1505 0 : hit = true;
1506 : }
1507 0 : if ( ( feature_smaller < best_greater ) && ( best_greater < feature_greater ) )
1508 : {
1509 0 : best_greater = feature_greater;
1510 0 : hit = true;
1511 : }
1512 : }
1513 : }
1514 :
1515 : /* move away from already layed-out parallel relationship-segments; */
1516 : /* iterate over the already created connectors */
1517 : layout_relationship_iter_t relationship_iterator;
1518 0 : layout_relationship_iter_copy( &relationship_iterator, &((*this_).already_processed) );
1519 0 : while ( layout_relationship_iter_has_next( &relationship_iterator ) )
1520 : {
1521 : /* get pointer to relationship */
1522 0 : const layout_relationship_t *const exist_relationship = layout_relationship_iter_next_ptr( &relationship_iterator );
1523 :
1524 : /* Note: This algorithm ignores the relationship types (same_type), sources and destinations (one_same_end) */
1525 0 : const geometry_connector_t *const exist_shape = layout_relationship_get_shape_const( exist_relationship );
1526 0 : if ( geometry_connector_is_intersecting_rectangle( exist_shape, &consider_rect ) )
1527 : {
1528 : const geometry_rectangle_t seg_1
1529 0 : = geometry_connector_get_segment_bounds( exist_shape, GEOMETRY_CONNECTOR_SEGMENT_SOURCE );
1530 : const bool seg_1_is_intersecting
1531 0 : = geometry_rectangle_is_intersecting( &seg_1, &consider_rect );
1532 0 : const double seg_1_smaller
1533 : = horizontal_line /* do vertical search if line is horizontal */
1534 0 : ? ( geometry_rectangle_get_top( &seg_1 ) - min_gap )
1535 0 : : ( geometry_rectangle_get_left( &seg_1 ) - min_gap );
1536 0 : const double seg_1_greater
1537 : = horizontal_line /* do vertical search if line is horizontal */
1538 0 : ? ( geometry_rectangle_get_bottom( &seg_1 ) + min_gap )
1539 0 : : ( geometry_rectangle_get_right( &seg_1 ) + min_gap );
1540 0 : if ( seg_1_is_intersecting
1541 0 : && ( seg_1_smaller < best_smaller ) && ( best_smaller < seg_1_greater ) )
1542 : {
1543 0 : best_smaller = seg_1_smaller;
1544 0 : hit = true;
1545 : }
1546 0 : if ( seg_1_is_intersecting
1547 0 : && ( seg_1_smaller < best_greater ) && ( best_greater < seg_1_greater ) )
1548 : {
1549 0 : best_greater = seg_1_greater;
1550 0 : hit = true;
1551 : }
1552 : const geometry_rectangle_t seg_2
1553 0 : = geometry_connector_get_segment_bounds( exist_shape, GEOMETRY_CONNECTOR_SEGMENT_MAIN );
1554 : const bool seg_2_is_intersecting
1555 0 : = geometry_rectangle_is_intersecting( &seg_2, &consider_rect );
1556 0 : const double seg_2_smaller
1557 : = horizontal_line /* do vertical search if line is horizontal */
1558 0 : ? ( geometry_rectangle_get_top( &seg_2 ) - min_gap )
1559 0 : : ( geometry_rectangle_get_left( &seg_2 ) - min_gap );
1560 0 : const double seg_2_greater
1561 : = horizontal_line /* do vertical search if line is horizontal */
1562 0 : ? ( geometry_rectangle_get_bottom( &seg_2 ) + min_gap )
1563 0 : : ( geometry_rectangle_get_right( &seg_2 ) + min_gap );
1564 0 : if ( seg_2_is_intersecting
1565 0 : && ( seg_2_smaller < best_smaller ) && ( best_smaller < seg_2_greater ) )
1566 : {
1567 0 : best_smaller = seg_2_smaller;
1568 0 : hit = true;
1569 : }
1570 0 : if ( seg_2_is_intersecting
1571 0 : && ( seg_2_smaller < best_greater ) && ( best_greater < seg_2_greater ) )
1572 : {
1573 0 : best_greater = seg_2_greater;
1574 0 : hit = true;
1575 : }
1576 : const geometry_rectangle_t seg_3
1577 0 : = geometry_connector_get_segment_bounds( exist_shape, GEOMETRY_CONNECTOR_SEGMENT_DESTINATION );
1578 : const bool seg_3_is_intersecting
1579 0 : = geometry_rectangle_is_intersecting( &seg_3, &consider_rect );
1580 0 : const double seg_3_smaller
1581 : = horizontal_line /* do vertical search if line is horizontal */
1582 0 : ? ( geometry_rectangle_get_top( &seg_3 ) - min_gap )
1583 0 : : ( geometry_rectangle_get_left( &seg_3 ) - min_gap );
1584 0 : const double seg_3_greater
1585 : = horizontal_line /* do vertical search if line is horizontal */
1586 0 : ? ( geometry_rectangle_get_bottom( &seg_3 ) + min_gap )
1587 0 : : ( geometry_rectangle_get_right( &seg_3 ) + min_gap );
1588 0 : if ( seg_3_is_intersecting
1589 0 : && ( seg_3_smaller < best_smaller ) && ( best_smaller < seg_3_greater ) )
1590 : {
1591 0 : best_smaller = seg_3_smaller;
1592 0 : hit = true;
1593 : }
1594 0 : if ( seg_3_is_intersecting
1595 0 : && ( seg_3_smaller < best_greater ) && ( best_greater < seg_3_greater ) )
1596 : {
1597 0 : best_greater = seg_3_greater;
1598 0 : hit = true;
1599 : }
1600 :
1601 0 : const geometry_3dir_t exist_dirs = geometry_connector_get_directions( exist_shape );
1602 0 : if ( horizontal_line )
1603 : {
1604 0 : const double exist_source_y = geometry_connector_get_main_line_source_y ( exist_shape );
1605 0 : const double exist_destination_y = geometry_connector_get_main_line_destination_y ( exist_shape );
1606 0 : if ( geometry_3dir_is_first_h( &exist_dirs ) || geometry_3dir_is_second_h( &exist_dirs ) )
1607 : {
1608 0 : if (( exist_source_y - min_gap < good_smaller )&&( good_smaller < exist_source_y + min_gap ))
1609 : {
1610 0 : good_smaller = exist_source_y - min_gap;
1611 0 : hit = true;
1612 : }
1613 0 : if (( exist_source_y - min_gap < good_greater )&&( good_greater < exist_source_y + min_gap ))
1614 : {
1615 0 : good_greater = exist_source_y + min_gap;
1616 0 : hit = true;
1617 : }
1618 : }
1619 0 : if ( geometry_3dir_is_third_h( &exist_dirs ) ) /* third segment only, second is already evaluated above */
1620 : {
1621 0 : if (( exist_destination_y - min_gap < good_smaller )&&( good_smaller < exist_destination_y + min_gap ))
1622 : {
1623 0 : good_smaller = exist_destination_y - min_gap;
1624 0 : hit = true;
1625 : }
1626 0 : if (( exist_destination_y - min_gap < good_greater )&&( good_greater < exist_destination_y + min_gap ))
1627 : {
1628 0 : good_greater = exist_destination_y + min_gap;
1629 0 : hit = true;
1630 : }
1631 : }
1632 : }
1633 : else
1634 : {
1635 0 : const double exist_source_x = geometry_connector_get_main_line_source_x ( exist_shape );
1636 0 : const double exist_destination_x = geometry_connector_get_main_line_destination_x ( exist_shape );
1637 0 : if ( geometry_3dir_is_first_v( &exist_dirs ) || geometry_3dir_is_second_v( &exist_dirs ) )
1638 : {
1639 0 : if (( exist_source_x - min_gap < good_smaller )&&( good_smaller < exist_source_x + min_gap ))
1640 : {
1641 0 : good_smaller = exist_source_x - min_gap;
1642 0 : hit = true;
1643 : }
1644 0 : if (( exist_source_x - min_gap < good_greater )&&( good_greater < exist_source_x + min_gap ))
1645 : {
1646 0 : good_greater = exist_source_x + min_gap;
1647 0 : hit = true;
1648 : }
1649 : }
1650 0 : if ( geometry_3dir_is_third_v( &exist_dirs ) ) /* third segment only, second is already evaluated above */
1651 : {
1652 0 : if (( exist_destination_x - min_gap < good_smaller )&&( good_smaller < exist_destination_x + min_gap ))
1653 : {
1654 0 : good_smaller = exist_destination_x - min_gap;
1655 0 : hit = true;
1656 : }
1657 0 : if (( exist_destination_x - min_gap < good_greater )&&( good_greater < exist_destination_x + min_gap ))
1658 : {
1659 0 : good_greater = exist_destination_x + min_gap;
1660 0 : hit = true;
1661 : }
1662 : }
1663 : }
1664 : }
1665 : }
1666 0 : layout_relationship_iter_destroy( &relationship_iterator );
1667 : }
1668 :
1669 : /* check success */
1670 0 : if ( best_greater < maximum_result )
1671 : {
1672 0 : if ( best_smaller > minimum_result )
1673 : {
1674 : /* best_greater and best_smaller are both in range; */
1675 : /* select the one with smaller distance to the center: */
1676 0 : if ( best_greater - center > center - best_smaller )
1677 : {
1678 0 : *io_coordinate = best_smaller;
1679 : }
1680 : else
1681 : {
1682 0 : *io_coordinate = best_greater;
1683 : }
1684 : }
1685 : else /* best_greater is in range */
1686 : {
1687 0 : *io_coordinate = best_greater;
1688 : }
1689 : }
1690 : else
1691 : {
1692 0 : if ( best_smaller > minimum_result )
1693 : {
1694 : /* best_smaller is in range */
1695 0 : *io_coordinate = best_smaller;
1696 : }
1697 : else
1698 : {
1699 : /* BOTH BEST VALUES ARE OUT OF RANGE */
1700 : /* CHECK GOOD VALUES: */
1701 0 : if ( good_greater > maximum_result )
1702 : {
1703 0 : if ( good_smaller < minimum_result )
1704 : {
1705 0 : err = U8_ERROR_NOT_FOUND;
1706 : }
1707 : else /* good_smaller is in range */
1708 : {
1709 0 : *io_coordinate = good_smaller;
1710 : }
1711 : }
1712 : else /* good_greater is in range */
1713 : {
1714 0 : if ( good_smaller < minimum_result )
1715 : {
1716 0 : *io_coordinate = good_greater;
1717 : }
1718 : else
1719 : {
1720 : /* good_smaller and good_greater are both in range; */
1721 : /* select the one with smaller distance to the center: */
1722 0 : if ( good_greater - center > center - good_smaller )
1723 : {
1724 0 : *io_coordinate = good_smaller;
1725 : }
1726 : else
1727 : {
1728 0 : *io_coordinate = good_greater;
1729 : }
1730 : }
1731 : }
1732 : }
1733 : }
1734 :
1735 0 : geometry_rectangle_destroy( &consider_rect );
1736 :
1737 0 : U8_TRACE_END_ERR(err);
1738 0 : return err;
1739 : }
1740 :
1741 0 : void pencil_relationship_2d_layouter_private_make_all_visible ( pencil_relationship_2d_layouter_t *this_ )
1742 : {
1743 0 : U8_TRACE_BEGIN();
1744 :
1745 : /* determine visibility */
1746 0 : const uint32_t count_relations = layout_visible_set_get_relationship_count ( (*this_).layout_data );
1747 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
1748 : {
1749 0 : layout_relationship_t *const the_relation = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
1750 0 : const layout_visible_classifier_t *const from_layout = layout_relationship_get_from_classifier_ptr ( the_relation );
1751 0 : const layout_visible_classifier_t *const to_layout = layout_relationship_get_to_classifier_ptr ( the_relation );
1752 0 : assert( from_layout != NULL );
1753 0 : assert( to_layout != NULL );
1754 0 : const data_visible_classifier_t *const from_data = layout_visible_classifier_get_data_const( from_layout );
1755 0 : const data_visible_classifier_t *const to_data = layout_visible_classifier_get_data_const( to_layout );
1756 0 : const data_diagramelement_t *const from_diagele = data_visible_classifier_get_diagramelement_const( from_data );
1757 0 : const data_diagramelement_t *const to_diagele = data_visible_classifier_get_diagramelement_const( to_data );
1758 0 : const data_diagramelement_flag_t from_flags = data_diagramelement_get_display_flags ( from_diagele );
1759 0 : const data_diagramelement_flag_t to_flags = data_diagramelement_get_display_flags ( to_diagele );
1760 0 : if (( 0 != ( DATA_DIAGRAMELEMENT_FLAG_GRAY_OUT & from_flags ))
1761 0 : || ( 0 != ( DATA_DIAGRAMELEMENT_FLAG_GRAY_OUT & to_flags )))
1762 : {
1763 0 : layout_relationship_set_visibility ( the_relation, PENCIL_VISIBILITY_GRAY_OUT );
1764 : }
1765 : else
1766 : {
1767 0 : layout_relationship_set_visibility ( the_relation, PENCIL_VISIBILITY_SHOW );
1768 : }
1769 : }
1770 :
1771 0 : U8_TRACE_END();
1772 0 : }
1773 :
1774 0 : void pencil_relationship_2d_layouter_layout_standard( pencil_relationship_2d_layouter_t *this_ )
1775 : {
1776 0 : U8_TRACE_BEGIN();
1777 :
1778 0 : pencil_relationship_2d_layouter_private_make_all_visible( this_ );
1779 :
1780 0 : pencil_relationship_2d_layouter_private_do_layout ( this_ );
1781 :
1782 0 : U8_TRACE_END();
1783 0 : }
1784 :
1785 0 : void pencil_relationship_2d_layouter_layout_void( pencil_relationship_2d_layouter_t *this_ )
1786 : {
1787 0 : U8_TRACE_BEGIN();
1788 :
1789 : /* hide all relationships */
1790 : const uint32_t count_relations
1791 0 : = layout_visible_set_get_relationship_count ( (*this_).layout_data );
1792 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
1793 : {
1794 : /*
1795 : layout_visible_set_set_relationship_visibility ( (*this_).layout_data, index, PENCIL_VISIBILITY_HIDE );
1796 : */
1797 0 : layout_visible_set_set_relationship_visibility ( (*this_).layout_data, index, PENCIL_VISIBILITY_IMPLICIT );
1798 : }
1799 :
1800 : /* layout the relationships (needed for PENCIL_VISIBILITY_IMPLICIT) */
1801 0 : pencil_relationship_2d_layouter_private_do_layout ( this_ );
1802 :
1803 0 : U8_TRACE_END();
1804 0 : }
1805 :
1806 0 : void pencil_relationship_2d_layouter_layout_for_communication( pencil_relationship_2d_layouter_t *this_ )
1807 : {
1808 0 : U8_TRACE_BEGIN();
1809 :
1810 0 : pencil_relationship_2d_layouter_private_make_all_visible( this_ );
1811 :
1812 : /* hide some relationships */
1813 : const uint32_t count_relations
1814 0 : = layout_visible_set_get_relationship_count ( (*this_).layout_data );
1815 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
1816 : {
1817 : layout_relationship_t *const the_relationship
1818 0 : = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
1819 :
1820 : /* adjust visibility */
1821 0 : if ( ( NULL == layout_relationship_get_from_feature_ptr ( the_relationship ) )
1822 0 : && ( NULL == layout_relationship_get_to_feature_ptr ( the_relationship ) ) )
1823 : {
1824 : /* this is a globally visible relation, not local/scenario-based */
1825 0 : layout_visible_set_set_relationship_visibility ( (*this_).layout_data, index, PENCIL_VISIBILITY_IMPLICIT );
1826 : }
1827 : }
1828 :
1829 : /* layout the visible relationships */
1830 0 : pencil_relationship_2d_layouter_private_do_layout ( this_ );
1831 :
1832 0 : U8_TRACE_END();
1833 0 : }
1834 :
1835 :
1836 : /*
1837 : Copyright 2017-2025 Andreas Warnke
1838 :
1839 : Licensed under the Apache License, Version 2.0 (the "License");
1840 : you may not use this file except in compliance with the License.
1841 : You may obtain a copy of the License at
1842 :
1843 : http://www.apache.org/licenses/LICENSE-2.0
1844 :
1845 : Unless required by applicable law or agreed to in writing, software
1846 : distributed under the License is distributed on an "AS IS" BASIS,
1847 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1848 : See the License for the specific language governing permissions and
1849 : limitations under the License.
1850 : */
|