Line data Source code
1 : /* File: pencil_rel_label_layouter.c; Copyright and License: see below */
2 :
3 : #include "pencil_rel_label_layouter.h"
4 : #include "layout/layout_relationship_iter.h"
5 : #include "geometry/geometry_point.h"
6 : #include "geometry/geometry_direction.h"
7 : #include "u8/u8_trace.h"
8 : #include "utf8stringbuf/utf8string.h"
9 :
10 0 : void pencil_rel_label_layouter_init( pencil_rel_label_layouter_t *this_,
11 : layout_visible_set_t *layout_data,
12 : const data_profile_part_t *profile,
13 : const pencil_size_t *pencil_size )
14 : {
15 0 : U8_TRACE_BEGIN();
16 0 : assert( NULL != layout_data );
17 0 : assert( NULL != profile );
18 0 : assert( NULL != pencil_size );
19 :
20 0 : (*this_).layout_data = layout_data;
21 0 : (*this_).profile = profile;
22 0 : (*this_).pencil_size = pencil_size;
23 0 : draw_relationship_label_init( &((*this_).draw_relationship_label) );
24 0 : pencil_floating_label_layouter_init_void( &((*this_).label_floater) );
25 :
26 0 : U8_TRACE_END();
27 0 : }
28 :
29 0 : void pencil_rel_label_layouter_reinit( pencil_rel_label_layouter_t *this_,
30 : layout_visible_set_t *layout_data,
31 : const data_profile_part_t *profile,
32 : const pencil_size_t *pencil_size )
33 : {
34 0 : U8_TRACE_BEGIN();
35 0 : assert( NULL != layout_data );
36 0 : assert( NULL != profile );
37 0 : assert( NULL != pencil_size );
38 :
39 0 : (*this_).layout_data = layout_data;
40 0 : (*this_).profile = profile;
41 0 : (*this_).pencil_size = pencil_size;
42 0 : pencil_floating_label_layouter_init_void( &((*this_).label_floater) );
43 :
44 0 : U8_TRACE_END();
45 0 : }
46 :
47 0 : void pencil_rel_label_layouter_destroy( pencil_rel_label_layouter_t *this_ )
48 : {
49 0 : U8_TRACE_BEGIN();
50 :
51 0 : pencil_floating_label_layouter_destroy ( &((*this_).label_floater) );
52 0 : draw_relationship_label_destroy( &((*this_).draw_relationship_label) );
53 :
54 0 : U8_TRACE_END();
55 0 : }
56 :
57 0 : void pencil_rel_label_layouter_do_layout( pencil_rel_label_layouter_t *this_, PangoLayout *font_layout )
58 : {
59 0 : U8_TRACE_BEGIN();
60 : assert ( (unsigned int) UNIVERSAL_ARRAY_INDEX_SORTER_MAX_ARRAY_SIZE >= (unsigned int) LAYOUT_VISIBLE_SET_MAX_RELATIONSHIPS );
61 0 : assert( NULL != font_layout );
62 :
63 0 : pencil_floating_label_layouter_reinit( &((*this_).label_floater),
64 : (*this_).layout_data,
65 : (*this_).profile,
66 : (*this_).pencil_size,
67 : font_layout
68 : );
69 :
70 : universal_array_index_sorter_t sorted;
71 0 : universal_array_index_sorter_init( &sorted );
72 :
73 : /* sort the relationships by their label-box layouting needs, drop invisible relations */
74 0 : pencil_rel_label_layouter_private_propose_processing_order ( this_, &sorted );
75 :
76 : /* layout the relationship label-boxes */
77 : layout_relationship_iter_t relationship_iterator;
78 0 : layout_relationship_iter_init( &relationship_iterator, (*this_).layout_data, &sorted );
79 0 : while ( layout_relationship_iter_has_next( &relationship_iterator ) )
80 : {
81 : /* determine pointer to relationship */
82 0 : layout_relationship_t *const current_relation = layout_relationship_iter_next_ptr( &relationship_iterator );
83 0 : geometry_point_t relation_middle = layout_relationship_get_middle ( current_relation );
84 :
85 : /* declaration of list of options */
86 0 : uint32_t solutions_count = 0;
87 : static const uint32_t SOLUTIONS_MAX = 10;
88 : geometry_rectangle_t solution[10];
89 :
90 : /* propose options */
91 0 : pencil_rel_label_layouter_private_propose_solutions( this_,
92 : current_relation,
93 : font_layout,
94 : SOLUTIONS_MAX,
95 : solution,
96 : &solutions_count
97 : );
98 :
99 : /* select best option */
100 : uint32_t index_of_best;
101 0 : if ( 1 == solutions_count )
102 : {
103 0 : index_of_best = 0;
104 : }
105 : else
106 : {
107 0 : pencil_floating_label_layouter_select_solution( &((*this_).label_floater),
108 : relation_middle,
109 : solutions_count,
110 : solution,
111 : &index_of_best
112 : );
113 : }
114 :
115 : /* store best option to (*this_).layout_data */
116 0 : layout_relationship_set_label_box( current_relation, &(solution[index_of_best]) );
117 : }
118 :
119 0 : layout_relationship_iter_destroy( &relationship_iterator );
120 0 : universal_array_index_sorter_destroy( &sorted );
121 :
122 0 : pencil_floating_label_layouter_reinit_void( &((*this_).label_floater) );
123 :
124 0 : U8_TRACE_END();
125 0 : }
126 :
127 0 : void pencil_rel_label_layouter_private_propose_processing_order( pencil_rel_label_layouter_t *this_,
128 : universal_array_index_sorter_t *out_sorted )
129 : {
130 0 : U8_TRACE_BEGIN();
131 0 : assert( NULL != out_sorted );
132 :
133 : /* sort the relationships by their label-box: the less simple, the earlier it shall be processed */
134 : const uint32_t count_relations
135 0 : = layout_visible_set_get_relationship_count ( (*this_).layout_data );
136 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
137 : {
138 : const layout_relationship_t *const current_relation
139 0 : = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
140 : const data_relationship_t *const relation_data
141 0 : = layout_relationship_get_data_const ( current_relation );
142 0 : assert( NULL != relation_data );
143 :
144 0 : int64_t simpleness = 0;
145 :
146 : /* determine simpleness by length of label */
147 0 : simpleness -= utf8string_get_length( data_relationship_get_name_const( relation_data ) );
148 :
149 : /* insert relation to sorted array, the simpler the more to the back */
150 0 : if ( PENCIL_VISIBILITY_HIDE != layout_relationship_get_visibility ( current_relation ) )
151 : {
152 : int insert_error;
153 0 : insert_error = universal_array_index_sorter_insert( out_sorted, index, simpleness );
154 0 : if ( 0 != insert_error )
155 : {
156 0 : U8_LOG_WARNING( "not all relationship label-boxes are layouted" );
157 : }
158 : }
159 : }
160 :
161 0 : U8_TRACE_END();
162 0 : }
163 :
164 0 : void pencil_rel_label_layouter_private_propose_solutions( pencil_rel_label_layouter_t *this_,
165 : layout_relationship_t *current_relation,
166 : PangoLayout *font_layout,
167 : uint32_t solutions_max,
168 : geometry_rectangle_t out_solutions[],
169 : uint32_t *out_solutions_count )
170 : {
171 0 : U8_TRACE_BEGIN();
172 0 : assert( NULL != current_relation );
173 0 : assert( NULL != font_layout );
174 0 : assert( NULL != out_solutions );
175 0 : assert( NULL != out_solutions_count );
176 :
177 0 : const data_relationship_t *the_relationship = layout_relationship_get_data_const( current_relation );
178 : {
179 : /* determine label dimensions */
180 0 : const geometry_dimensions_t label_dim_proposal = {
181 0 : .width = 30.0 * pencil_size_get_standard_font_size( (*this_).pencil_size ),
182 0 : .height = pencil_size_get_standard_font_size( (*this_).pencil_size )
183 : };
184 : geometry_dimensions_t preferred_label_dim;
185 0 : draw_relationship_label_get_type_and_name_dimensions( &((*this_).draw_relationship_label),
186 : the_relationship,
187 : (*this_).profile,
188 : &label_dim_proposal,
189 : (*this_).pencil_size,
190 : font_layout,
191 : &preferred_label_dim
192 : );
193 :
194 : /* get layout data */
195 0 : const double line_w = 0.5 * pencil_size_get_standard_line_width( (*this_).pencil_size );
196 0 : const double border_gap = pencil_size_get_standard_object_border( (*this_).pencil_size );
197 0 : const double label_dist = line_w + border_gap; /* distance from label-test to line */
198 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
199 :
200 : /* get connector data */
201 0 : const geometry_connector_t *const shape = layout_relationship_get_shape_const ( current_relation );
202 0 : const double source_end_x = geometry_connector_get_source_end_x ( shape );
203 0 : const double source_end_y = geometry_connector_get_source_end_y ( shape );
204 0 : const double main_line_source_x = geometry_connector_get_main_line_source_x ( shape );
205 0 : const double main_line_source_y = geometry_connector_get_main_line_source_y ( shape );
206 0 : const double main_line_destination_x = geometry_connector_get_main_line_destination_x ( shape );
207 0 : const double main_line_destination_y = geometry_connector_get_main_line_destination_y ( shape );
208 0 : const double destination_end_x = geometry_connector_get_destination_end_x ( shape );
209 0 : const double destination_end_y = geometry_connector_get_destination_end_y ( shape );
210 : geometry_point_t src_end;
211 : geometry_point_t main_src;
212 : geometry_point_t main_dst;
213 : geometry_point_t dst_end;
214 0 : geometry_point_init ( &src_end, source_end_x, source_end_y );
215 0 : geometry_point_init ( &main_src, main_line_source_x, main_line_source_y );
216 0 : geometry_point_init ( &main_dst, main_line_destination_x, main_line_destination_y );
217 0 : geometry_point_init ( &dst_end, destination_end_x, destination_end_y );
218 0 : const geometry_3dir_t connector_dirs = geometry_connector_get_directions( shape );
219 0 : const geometry_direction_t src_dir = geometry_3dir_get_first ( &connector_dirs );
220 0 : const geometry_direction_t main_dir = geometry_3dir_get_second ( &connector_dirs );
221 0 : const geometry_direction_t dst_dir = geometry_3dir_get_third ( &connector_dirs );
222 :
223 : /* propose solutions */
224 0 : assert( solutions_max >= 10 );
225 0 : uint32_t solution_idx = 0;
226 :
227 : /* there are 0 or 4 solutions at the src line segment */
228 0 : if ( geometry_point_calc_chess_distance( &src_end, &main_src ) > object_dist )
229 : {
230 : /* this is a noteworthy line segment */
231 : geometry_anchor_t anchor_1;
232 : geometry_anchor_t anchor_2;
233 : geometry_anchor_t anchor_3;
234 : geometry_anchor_t anchor_4;
235 :
236 0 : if ( ( src_dir == GEOMETRY_DIRECTION_UP ) || ( src_dir == GEOMETRY_DIRECTION_DOWN ) )
237 : {
238 : /* right */
239 0 : geometry_anchor_init( &anchor_1,
240 : main_line_source_x + label_dist,
241 0 : (source_end_y + main_line_source_y) / 2.0,
242 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
243 : GEOMETRY_V_ALIGN_CENTER
244 : );
245 :
246 : /* left */
247 0 : geometry_anchor_init( &anchor_2,
248 : main_line_source_x - label_dist,
249 0 : (source_end_y + main_line_source_y) / 2.0,
250 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
251 : GEOMETRY_V_ALIGN_CENTER
252 : );
253 : /* at bend to main line */
254 0 : geometry_anchor_init( &anchor_3,
255 : main_line_source_x,
256 0 : main_line_source_y + (( src_dir == GEOMETRY_DIRECTION_UP ) ? (-label_dist) : label_dist ),
257 : GEOMETRY_H_ALIGN_CENTER,
258 : ( src_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_BOTTOM : GEOMETRY_V_ALIGN_TOP
259 : );
260 0 : geometry_anchor_init( &anchor_4,
261 0 : main_line_source_x + (( main_dir == GEOMETRY_DIRECTION_LEFT ) ? label_dist : (-label_dist) ),
262 : main_line_source_y,
263 : ( main_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_LEFT : GEOMETRY_H_ALIGN_RIGHT,
264 : GEOMETRY_V_ALIGN_CENTER
265 : );
266 : }
267 : else
268 : {
269 : /* down */
270 0 : geometry_anchor_init( &anchor_1,
271 0 : (source_end_x + main_line_source_x) / 2.0,
272 : main_line_source_y + label_dist,
273 : GEOMETRY_H_ALIGN_CENTER,
274 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
275 : );
276 :
277 : /* up */
278 0 : geometry_anchor_init( &anchor_2,
279 0 : (source_end_x + main_line_source_x) / 2.0,
280 : main_line_source_y - label_dist,
281 : GEOMETRY_H_ALIGN_CENTER,
282 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
283 : );
284 : /* at bend to main line */
285 0 : geometry_anchor_init( &anchor_3,
286 0 : main_line_source_x + (( src_dir == GEOMETRY_DIRECTION_LEFT ) ? (-label_dist) : label_dist ),
287 : main_line_source_y,
288 : ( src_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_RIGHT : GEOMETRY_H_ALIGN_LEFT,
289 : GEOMETRY_V_ALIGN_CENTER
290 : );
291 0 : geometry_anchor_init( &anchor_4,
292 : main_line_source_x,
293 0 : main_line_source_y + (( main_dir == GEOMETRY_DIRECTION_UP ) ? label_dist : (-label_dist) ),
294 : GEOMETRY_H_ALIGN_CENTER,
295 : ( main_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_TOP : GEOMETRY_V_ALIGN_BOTTOM
296 : );
297 : }
298 :
299 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
300 : &anchor_1,
301 : &preferred_label_dim,
302 : &((*this_).draw_relationship_label),
303 : the_relationship,
304 0 : &(out_solutions[solution_idx])
305 : );
306 0 : solution_idx ++;
307 :
308 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
309 : &anchor_2,
310 : &preferred_label_dim,
311 : &((*this_).draw_relationship_label),
312 : the_relationship,
313 0 : &(out_solutions[solution_idx])
314 : );
315 0 : solution_idx ++;
316 :
317 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
318 : &anchor_3,
319 : &preferred_label_dim,
320 : &((*this_).draw_relationship_label),
321 : the_relationship,
322 0 : &(out_solutions[solution_idx])
323 : );
324 0 : solution_idx ++;
325 :
326 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
327 : &anchor_4,
328 : &preferred_label_dim,
329 : &((*this_).draw_relationship_label),
330 : the_relationship,
331 0 : &(out_solutions[solution_idx])
332 : );
333 0 : solution_idx ++;
334 : }
335 :
336 : /* there are 2 solutions at the main line segment */
337 : {
338 : geometry_anchor_t anchor_5;
339 : geometry_anchor_t anchor_6;
340 :
341 0 : if ( ( main_dir == GEOMETRY_DIRECTION_UP ) || ( main_dir == GEOMETRY_DIRECTION_DOWN ) )
342 : {
343 0 : geometry_anchor_init( &anchor_5,
344 : main_line_source_x + label_dist,
345 0 : (main_line_source_y + main_line_destination_y) / 2.0,
346 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
347 : GEOMETRY_V_ALIGN_CENTER
348 : );
349 0 : geometry_anchor_init( &anchor_6,
350 : main_line_source_x - label_dist,
351 0 : (main_line_source_y + main_line_destination_y) / 2.0,
352 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
353 : GEOMETRY_V_ALIGN_CENTER
354 : );
355 : }
356 : else
357 : {
358 0 : geometry_anchor_init( &anchor_5,
359 0 : (main_line_source_x + main_line_destination_x) / 2.0,
360 : main_line_source_y + label_dist,
361 : GEOMETRY_H_ALIGN_CENTER,
362 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
363 : );
364 0 : geometry_anchor_init( &anchor_6,
365 0 : (main_line_source_x + main_line_destination_x) / 2.0,
366 : main_line_source_y - label_dist,
367 : GEOMETRY_H_ALIGN_CENTER,
368 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
369 : );
370 : }
371 :
372 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
373 : &anchor_5,
374 : &preferred_label_dim,
375 : &((*this_).draw_relationship_label),
376 : the_relationship,
377 0 : &(out_solutions[solution_idx])
378 : );
379 0 : solution_idx ++;
380 :
381 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
382 : &anchor_6,
383 : &preferred_label_dim,
384 : &((*this_).draw_relationship_label),
385 : the_relationship,
386 0 : &(out_solutions[solution_idx])
387 : );
388 0 : solution_idx ++;
389 : }
390 :
391 : /* there are 0 or 4 solutions at the dst line segment */
392 0 : if ( geometry_point_calc_chess_distance( &main_dst, &dst_end ) > object_dist )
393 : {
394 : geometry_anchor_t anchor_7;
395 : geometry_anchor_t anchor_8;
396 : geometry_anchor_t anchor_9;
397 : geometry_anchor_t anchor_10;
398 :
399 : /* this is a noteworthy line segment */
400 0 : if ( ( dst_dir == GEOMETRY_DIRECTION_UP ) || ( dst_dir == GEOMETRY_DIRECTION_DOWN ) )
401 : {
402 : /* at bend to main line */
403 0 : geometry_anchor_init( &anchor_7,
404 0 : main_line_destination_x + (( main_dir == GEOMETRY_DIRECTION_LEFT ) ? (-label_dist) : label_dist ),
405 : main_line_destination_y,
406 : ( main_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_RIGHT : GEOMETRY_H_ALIGN_LEFT,
407 : GEOMETRY_V_ALIGN_CENTER
408 : );
409 0 : geometry_anchor_init( &anchor_8,
410 : main_line_destination_x,
411 0 : main_line_destination_y + (( dst_dir == GEOMETRY_DIRECTION_UP ) ? label_dist : (-label_dist) ),
412 : GEOMETRY_H_ALIGN_CENTER,
413 : ( dst_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_TOP : GEOMETRY_V_ALIGN_BOTTOM
414 : );
415 : /* right */
416 0 : geometry_anchor_init( &anchor_9,
417 : main_line_destination_x + label_dist,
418 0 : (destination_end_y + main_line_destination_y) / 2.0,
419 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
420 : GEOMETRY_V_ALIGN_CENTER
421 : );
422 : /* left */
423 0 : geometry_anchor_init( &anchor_10,
424 : main_line_destination_x - label_dist,
425 0 : (destination_end_y + main_line_destination_y) / 2.0,
426 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
427 : GEOMETRY_V_ALIGN_CENTER
428 : );
429 : }
430 : else
431 : {
432 : /* at bend to main line */
433 0 : geometry_anchor_init( &anchor_7,
434 : main_line_destination_x,
435 0 : main_line_destination_y + (( main_dir == GEOMETRY_DIRECTION_UP ) ? (-label_dist) : label_dist ),
436 : GEOMETRY_H_ALIGN_CENTER,
437 : ( main_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_BOTTOM : GEOMETRY_V_ALIGN_TOP
438 : );
439 0 : geometry_anchor_init( &anchor_8,
440 0 : main_line_destination_x + (( dst_dir == GEOMETRY_DIRECTION_LEFT ) ? label_dist : (-label_dist) ),
441 : main_line_destination_y,
442 : ( dst_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_LEFT : GEOMETRY_H_ALIGN_RIGHT,
443 : GEOMETRY_V_ALIGN_CENTER
444 : );
445 : /* down */
446 0 : geometry_anchor_init( &anchor_9,
447 0 : (destination_end_x + main_line_destination_x) / 2.0,
448 : main_line_destination_y + label_dist,
449 : GEOMETRY_H_ALIGN_CENTER,
450 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
451 : );
452 :
453 : /* up */
454 0 : geometry_anchor_init( &anchor_10,
455 0 : (destination_end_x + main_line_destination_x) / 2.0,
456 : main_line_destination_y - label_dist,
457 : GEOMETRY_H_ALIGN_CENTER,
458 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
459 : );
460 :
461 : }
462 :
463 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
464 : &anchor_7,
465 : &preferred_label_dim,
466 : &((*this_).draw_relationship_label),
467 : the_relationship,
468 0 : &(out_solutions[solution_idx])
469 : );
470 0 : solution_idx ++;
471 :
472 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
473 : &anchor_8,
474 : &preferred_label_dim,
475 : &((*this_).draw_relationship_label),
476 : the_relationship,
477 0 : &(out_solutions[solution_idx])
478 : );
479 0 : solution_idx ++;
480 :
481 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
482 : &anchor_9,
483 : &preferred_label_dim,
484 : &((*this_).draw_relationship_label),
485 : the_relationship,
486 0 : &(out_solutions[solution_idx])
487 : );
488 0 : solution_idx ++;
489 :
490 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
491 : &anchor_10,
492 : &preferred_label_dim,
493 : &((*this_).draw_relationship_label),
494 : the_relationship,
495 0 : &(out_solutions[solution_idx])
496 : );
497 0 : solution_idx ++;
498 : }
499 :
500 0 : assert( solution_idx > 0 );
501 0 : assert( solution_idx <= solutions_max );
502 0 : assert(( solution_idx == 2 )||( solution_idx == 6 )||( solution_idx == 10 ));
503 0 : *out_solutions_count = solution_idx;
504 : }
505 :
506 0 : U8_TRACE_END();
507 0 : }
508 :
509 :
510 : /*
511 : Copyright 2019-2026 Andreas Warnke
512 :
513 : Licensed under the Apache License, Version 2.0 (the "License");
514 : you may not use this file except in compliance with the License.
515 : You may obtain a copy of the License at
516 :
517 : http://www.apache.org/licenses/LICENSE-2.0
518 :
519 : Unless required by applicable law or agreed to in writing, software
520 : distributed under the License is distributed on an "AS IS" BASIS,
521 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
522 : See the License for the specific language governing permissions and
523 : limitations under the License.
524 : */
|