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 object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
197 :
198 : /* get connector data */
199 0 : const geometry_connector_t *const shape = layout_relationship_get_shape_const ( current_relation );
200 0 : const double source_end_x = geometry_connector_get_source_end_x ( shape );
201 0 : const double source_end_y = geometry_connector_get_source_end_y ( shape );
202 0 : const double main_line_source_x = geometry_connector_get_main_line_source_x ( shape );
203 0 : const double main_line_source_y = geometry_connector_get_main_line_source_y ( shape );
204 0 : const double main_line_destination_x = geometry_connector_get_main_line_destination_x ( shape );
205 0 : const double main_line_destination_y = geometry_connector_get_main_line_destination_y ( shape );
206 0 : const double destination_end_x = geometry_connector_get_destination_end_x ( shape );
207 0 : const double destination_end_y = geometry_connector_get_destination_end_y ( shape );
208 : geometry_point_t src_end;
209 : geometry_point_t main_src;
210 : geometry_point_t main_dst;
211 : geometry_point_t dst_end;
212 0 : geometry_point_init ( &src_end, source_end_x, source_end_y );
213 0 : geometry_point_init ( &main_src, main_line_source_x, main_line_source_y );
214 0 : geometry_point_init ( &main_dst, main_line_destination_x, main_line_destination_y );
215 0 : geometry_point_init ( &dst_end, destination_end_x, destination_end_y );
216 0 : const geometry_3dir_t connector_dirs = geometry_connector_get_directions( shape );
217 0 : const geometry_direction_t src_dir = geometry_3dir_get_first ( &connector_dirs );
218 0 : const geometry_direction_t main_dir = geometry_3dir_get_second ( &connector_dirs );
219 0 : const geometry_direction_t dst_dir = geometry_3dir_get_third ( &connector_dirs );
220 :
221 : /* propose solutions */
222 0 : assert( solutions_max >= 10 );
223 0 : uint32_t solution_idx = 0;
224 :
225 : /* there are 0 or 3 solutions at the src line segment */
226 0 : if ( geometry_point_calc_chess_distance( &src_end, &main_src ) > object_dist )
227 : {
228 : /* this is a noteworthy line segment */
229 : geometry_anchor_t anchor_1;
230 : geometry_anchor_t anchor_2;
231 : geometry_anchor_t anchor_3;
232 : geometry_anchor_t anchor_4;
233 :
234 0 : if ( ( src_dir == GEOMETRY_DIRECTION_UP ) || ( src_dir == GEOMETRY_DIRECTION_DOWN ) )
235 : {
236 : /* right */
237 0 : geometry_anchor_init( &anchor_1,
238 : main_line_source_x + line_w,
239 0 : (source_end_y + main_line_source_y) / 2.0,
240 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
241 : GEOMETRY_V_ALIGN_CENTER
242 : );
243 :
244 : /* left */
245 0 : geometry_anchor_init( &anchor_2,
246 : main_line_source_x - line_w,
247 0 : (source_end_y + main_line_source_y) / 2.0,
248 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
249 : GEOMETRY_V_ALIGN_CENTER
250 : );
251 : /* at bend to main line */
252 0 : geometry_anchor_init( &anchor_3,
253 : main_line_source_x,
254 0 : main_line_source_y + (( src_dir == GEOMETRY_DIRECTION_UP ) ? (-line_w) : line_w ),
255 : GEOMETRY_H_ALIGN_CENTER,
256 : ( src_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_BOTTOM : GEOMETRY_V_ALIGN_TOP
257 : );
258 0 : geometry_anchor_init( &anchor_4,
259 0 : main_line_source_x + (( main_dir == GEOMETRY_DIRECTION_LEFT ) ? line_w : (-line_w) ),
260 : main_line_source_y,
261 : ( main_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_LEFT : GEOMETRY_H_ALIGN_RIGHT,
262 : GEOMETRY_V_ALIGN_CENTER
263 : );
264 : }
265 : else
266 : {
267 : /* down */
268 0 : geometry_anchor_init( &anchor_1,
269 0 : (source_end_x + main_line_source_x) / 2.0,
270 : main_line_source_y + line_w,
271 : GEOMETRY_H_ALIGN_CENTER,
272 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
273 : );
274 :
275 : /* up */
276 0 : geometry_anchor_init( &anchor_2,
277 0 : (source_end_x + main_line_source_x) / 2.0,
278 : main_line_source_y - line_w,
279 : GEOMETRY_H_ALIGN_CENTER,
280 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
281 : );
282 : /* at bend to main line */
283 0 : geometry_anchor_init( &anchor_3,
284 0 : main_line_source_x + (( src_dir == GEOMETRY_DIRECTION_LEFT ) ? (-line_w) : line_w ),
285 : main_line_source_y,
286 : ( src_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_RIGHT : GEOMETRY_H_ALIGN_LEFT,
287 : GEOMETRY_V_ALIGN_CENTER
288 : );
289 0 : geometry_anchor_init( &anchor_4,
290 : main_line_source_x,
291 0 : main_line_source_y + (( main_dir == GEOMETRY_DIRECTION_UP ) ? line_w : (-line_w) ),
292 : GEOMETRY_H_ALIGN_CENTER,
293 : ( main_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_TOP : GEOMETRY_V_ALIGN_BOTTOM
294 : );
295 : }
296 :
297 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
298 : &anchor_1,
299 : &preferred_label_dim,
300 : &((*this_).draw_relationship_label),
301 : the_relationship,
302 0 : &(out_solutions[solution_idx])
303 : );
304 0 : solution_idx ++;
305 :
306 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
307 : &anchor_2,
308 : &preferred_label_dim,
309 : &((*this_).draw_relationship_label),
310 : the_relationship,
311 0 : &(out_solutions[solution_idx])
312 : );
313 0 : solution_idx ++;
314 :
315 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
316 : &anchor_3,
317 : &preferred_label_dim,
318 : &((*this_).draw_relationship_label),
319 : the_relationship,
320 0 : &(out_solutions[solution_idx])
321 : );
322 0 : solution_idx ++;
323 :
324 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
325 : &anchor_4,
326 : &preferred_label_dim,
327 : &((*this_).draw_relationship_label),
328 : the_relationship,
329 0 : &(out_solutions[solution_idx])
330 : );
331 0 : solution_idx ++;
332 : }
333 :
334 : /* there are 2 solutions at the main line segment */
335 : {
336 : geometry_anchor_t anchor_5;
337 : geometry_anchor_t anchor_6;
338 :
339 0 : if ( ( main_dir == GEOMETRY_DIRECTION_UP ) || ( main_dir == GEOMETRY_DIRECTION_DOWN ) )
340 : {
341 0 : geometry_anchor_init( &anchor_5,
342 : main_line_source_x + line_w,
343 0 : (main_line_source_y + main_line_destination_y) / 2.0,
344 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
345 : GEOMETRY_V_ALIGN_CENTER
346 : );
347 0 : geometry_anchor_init( &anchor_6,
348 : main_line_source_x - line_w,
349 0 : (main_line_source_y + main_line_destination_y) / 2.0,
350 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
351 : GEOMETRY_V_ALIGN_CENTER
352 : );
353 : }
354 : else
355 : {
356 0 : geometry_anchor_init( &anchor_5,
357 0 : (main_line_source_x + main_line_destination_x) / 2.0,
358 : main_line_source_y + line_w,
359 : GEOMETRY_H_ALIGN_CENTER,
360 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
361 : );
362 0 : geometry_anchor_init( &anchor_6,
363 0 : (main_line_source_x + main_line_destination_x) / 2.0,
364 : main_line_source_y - line_w,
365 : GEOMETRY_H_ALIGN_CENTER,
366 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
367 : );
368 : }
369 :
370 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
371 : &anchor_5,
372 : &preferred_label_dim,
373 : &((*this_).draw_relationship_label),
374 : the_relationship,
375 0 : &(out_solutions[solution_idx])
376 : );
377 0 : solution_idx ++;
378 :
379 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
380 : &anchor_6,
381 : &preferred_label_dim,
382 : &((*this_).draw_relationship_label),
383 : the_relationship,
384 0 : &(out_solutions[solution_idx])
385 : );
386 0 : solution_idx ++;
387 : }
388 :
389 : /* there are 0 or 3 solutions at the dst line segment */
390 0 : if ( geometry_point_calc_chess_distance( &main_dst, &dst_end ) > object_dist )
391 : {
392 : geometry_anchor_t anchor_7;
393 : geometry_anchor_t anchor_8;
394 : geometry_anchor_t anchor_9;
395 : geometry_anchor_t anchor_10;
396 :
397 : /* this is a noteworthy line segment */
398 0 : if ( ( dst_dir == GEOMETRY_DIRECTION_UP ) || ( dst_dir == GEOMETRY_DIRECTION_DOWN ) )
399 : {
400 : /* at bend to main line */
401 0 : geometry_anchor_init( &anchor_7,
402 0 : main_line_destination_x + (( main_dir == GEOMETRY_DIRECTION_LEFT ) ? (-line_w) : line_w ),
403 : main_line_destination_y,
404 : ( main_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_RIGHT : GEOMETRY_H_ALIGN_LEFT,
405 : GEOMETRY_V_ALIGN_CENTER
406 : );
407 0 : geometry_anchor_init( &anchor_8,
408 : main_line_destination_x,
409 0 : main_line_destination_y + (( dst_dir == GEOMETRY_DIRECTION_UP ) ? line_w : (-line_w) ),
410 : GEOMETRY_H_ALIGN_CENTER,
411 : ( dst_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_TOP : GEOMETRY_V_ALIGN_BOTTOM
412 : );
413 : /* right */
414 0 : geometry_anchor_init( &anchor_9,
415 : main_line_destination_x + line_w,
416 0 : (destination_end_y + main_line_destination_y) / 2.0,
417 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
418 : GEOMETRY_V_ALIGN_CENTER
419 : );
420 : /* left */
421 0 : geometry_anchor_init( &anchor_10,
422 : main_line_destination_x - line_w,
423 0 : (destination_end_y + main_line_destination_y) / 2.0,
424 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
425 : GEOMETRY_V_ALIGN_CENTER
426 : );
427 : }
428 : else
429 : {
430 : /* at bend to main line */
431 0 : geometry_anchor_init( &anchor_7,
432 : main_line_destination_x,
433 0 : main_line_destination_y + (( main_dir == GEOMETRY_DIRECTION_UP ) ? (-line_w) : line_w ),
434 : GEOMETRY_H_ALIGN_CENTER,
435 : ( main_dir == GEOMETRY_DIRECTION_UP ) ? GEOMETRY_V_ALIGN_BOTTOM : GEOMETRY_V_ALIGN_TOP
436 : );
437 0 : geometry_anchor_init( &anchor_8,
438 0 : main_line_destination_x + (( dst_dir == GEOMETRY_DIRECTION_LEFT ) ? line_w : (-line_w) ),
439 : main_line_destination_y,
440 : ( dst_dir == GEOMETRY_DIRECTION_LEFT ) ? GEOMETRY_H_ALIGN_LEFT : GEOMETRY_H_ALIGN_RIGHT,
441 : GEOMETRY_V_ALIGN_CENTER
442 : );
443 : /* down */
444 0 : geometry_anchor_init( &anchor_9,
445 0 : (destination_end_x + main_line_destination_x) / 2.0,
446 : main_line_destination_y + line_w,
447 : GEOMETRY_H_ALIGN_CENTER,
448 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top of the label */
449 : );
450 :
451 : /* up */
452 0 : geometry_anchor_init( &anchor_10,
453 0 : (destination_end_x + main_line_destination_x) / 2.0,
454 : main_line_destination_y - line_w,
455 : GEOMETRY_H_ALIGN_CENTER,
456 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom of the label */
457 : );
458 :
459 : }
460 :
461 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
462 : &anchor_7,
463 : &preferred_label_dim,
464 : &((*this_).draw_relationship_label),
465 : the_relationship,
466 0 : &(out_solutions[solution_idx])
467 : );
468 0 : solution_idx ++;
469 :
470 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
471 : &anchor_8,
472 : &preferred_label_dim,
473 : &((*this_).draw_relationship_label),
474 : the_relationship,
475 0 : &(out_solutions[solution_idx])
476 : );
477 0 : solution_idx ++;
478 :
479 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
480 : &anchor_9,
481 : &preferred_label_dim,
482 : &((*this_).draw_relationship_label),
483 : the_relationship,
484 0 : &(out_solutions[solution_idx])
485 : );
486 0 : solution_idx ++;
487 :
488 0 : pencil_floating_label_layouter_propose_solution_rel( &((*this_).label_floater),
489 : &anchor_10,
490 : &preferred_label_dim,
491 : &((*this_).draw_relationship_label),
492 : the_relationship,
493 0 : &(out_solutions[solution_idx])
494 : );
495 0 : solution_idx ++;
496 : }
497 :
498 0 : assert( solution_idx > 0 );
499 0 : assert( solution_idx <= solutions_max );
500 0 : assert(( solution_idx == 2 )||( solution_idx == 6 )||( solution_idx == 10 ));
501 0 : *out_solutions_count = solution_idx;
502 : }
503 :
504 0 : U8_TRACE_END();
505 0 : }
506 :
507 :
508 : /*
509 : Copyright 2019-2025 Andreas Warnke
510 :
511 : Licensed under the Apache License, Version 2.0 (the "License");
512 : you may not use this file except in compliance with the License.
513 : You may obtain a copy of the License at
514 :
515 : http://www.apache.org/licenses/LICENSE-2.0
516 :
517 : Unless required by applicable law or agreed to in writing, software
518 : distributed under the License is distributed on an "AS IS" BASIS,
519 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
520 : See the License for the specific language governing permissions and
521 : limitations under the License.
522 : */
|