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 "geometry/geometry_point.h"
5 : #include "geometry/geometry_direction.h"
6 : #include "u8/u8_trace.h"
7 : #include "utf8stringbuf/utf8string.h"
8 :
9 0 : void pencil_rel_label_layouter_init( pencil_rel_label_layouter_t *this_,
10 : layout_visible_set_t *layout_data,
11 : const data_profile_part_t *profile,
12 : const pencil_size_t *pencil_size )
13 : {
14 0 : U8_TRACE_BEGIN();
15 0 : assert( NULL != layout_data );
16 0 : assert( NULL != profile );
17 0 : assert( NULL != pencil_size );
18 :
19 0 : (*this_).layout_data = layout_data;
20 0 : (*this_).profile = profile;
21 0 : (*this_).pencil_size = pencil_size;
22 0 : draw_relationship_label_init( &((*this_).draw_relationship_label) );
23 0 : pencil_label_layout_helper_init ( &((*this_).label_layout_helper) );
24 :
25 0 : U8_TRACE_END();
26 0 : }
27 :
28 0 : void pencil_rel_label_layouter_reinit( pencil_rel_label_layouter_t *this_,
29 : layout_visible_set_t *layout_data,
30 : const data_profile_part_t *profile,
31 : const pencil_size_t *pencil_size )
32 : {
33 0 : U8_TRACE_BEGIN();
34 0 : assert( NULL != layout_data );
35 0 : assert( NULL != profile );
36 0 : assert( NULL != pencil_size );
37 :
38 0 : (*this_).layout_data = layout_data;
39 0 : (*this_).profile = profile;
40 0 : (*this_).pencil_size = pencil_size;
41 :
42 0 : U8_TRACE_END();
43 0 : }
44 :
45 0 : void pencil_rel_label_layouter_destroy( pencil_rel_label_layouter_t *this_ )
46 : {
47 0 : U8_TRACE_BEGIN();
48 :
49 0 : pencil_label_layout_helper_destroy ( &((*this_).label_layout_helper) );
50 0 : draw_relationship_label_destroy( &((*this_).draw_relationship_label) );
51 :
52 0 : U8_TRACE_END();
53 0 : }
54 :
55 0 : void pencil_rel_label_layouter_do_layout ( pencil_rel_label_layouter_t *this_, PangoLayout *font_layout )
56 : {
57 0 : U8_TRACE_BEGIN();
58 : assert ( (unsigned int) UNIVERSAL_ARRAY_INDEX_SORTER_MAX_ARRAY_SIZE >= (unsigned int) LAYOUT_VISIBLE_SET_MAX_RELATIONSHIPS );
59 0 : assert( NULL != font_layout );
60 :
61 : universal_array_index_sorter_t sorted;
62 0 : universal_array_index_sorter_init( &sorted );
63 :
64 : /* sort the relationships by their label-box layouting needs, drop invisible relations */
65 0 : pencil_rel_label_layouter_private_propose_processing_order ( this_, &sorted );
66 :
67 : /* layout the relationship label-boxes */
68 : const uint32_t count_sorted
69 0 : = universal_array_index_sorter_get_count( &sorted );
70 0 : for ( uint32_t sort_index = 0; sort_index < count_sorted; sort_index ++ )
71 : {
72 : /* determine pointer to relationship */
73 : const uint32_t index
74 0 : = universal_array_index_sorter_get_array_index( &sorted, sort_index );
75 : layout_relationship_t *const current_relation
76 0 : = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
77 0 : geometry_point_t relation_middle = layout_relationship_get_middle ( current_relation );
78 :
79 : /* declaration of list of options */
80 0 : uint32_t solutions_count = 0;
81 : static const uint32_t SOLUTIONS_MAX = 16;
82 : geometry_rectangle_t solution[16];
83 :
84 : /* propose options */
85 0 : pencil_rel_label_layouter_private_propose_solutions ( this_,
86 : current_relation,
87 : font_layout,
88 : SOLUTIONS_MAX,
89 : solution,
90 : &solutions_count
91 : );
92 :
93 : /* select best option */
94 : uint32_t index_of_best;
95 0 : if ( 1 == solutions_count )
96 : {
97 0 : index_of_best = 0;
98 : }
99 : else
100 : {
101 0 : pencil_label_layout_helper_select_solution ( &((*this_).label_layout_helper),
102 : (*this_).layout_data,
103 : relation_middle,
104 : solutions_count,
105 : solution,
106 : &index_of_best
107 : );
108 : }
109 :
110 : /* store best option to (*this_).layout_data */
111 0 : layout_relationship_set_label_box( current_relation, &(solution[index_of_best]) );
112 : }
113 :
114 0 : universal_array_index_sorter_destroy( &sorted );
115 :
116 0 : U8_TRACE_END();
117 0 : }
118 :
119 0 : void pencil_rel_label_layouter_private_propose_processing_order ( pencil_rel_label_layouter_t *this_,
120 : universal_array_index_sorter_t *out_sorted )
121 : {
122 0 : U8_TRACE_BEGIN();
123 0 : assert( NULL != out_sorted );
124 :
125 : /* sort the relationships by their label-box: the less simple, the earlier it shall be processed */
126 : const uint32_t count_relations
127 0 : = layout_visible_set_get_relationship_count ( (*this_).layout_data );
128 0 : for ( uint32_t index = 0; index < count_relations; index ++ )
129 : {
130 : const layout_relationship_t *const current_relation
131 0 : = layout_visible_set_get_relationship_ptr ( (*this_).layout_data, index );
132 : const data_relationship_t *const relation_data
133 0 : = layout_relationship_get_data_const ( current_relation );
134 0 : assert( NULL != relation_data );
135 :
136 0 : int64_t simpleness = 0;
137 :
138 : /* determine simpleness by length of label */
139 0 : simpleness -= utf8string_get_length( data_relationship_get_name_const( relation_data ) );
140 :
141 : /* insert relation to sorted array, the simpler the more to the back */
142 0 : if ( PENCIL_VISIBILITY_HIDE != layout_relationship_get_visibility ( current_relation ) )
143 : {
144 : int insert_error;
145 0 : insert_error = universal_array_index_sorter_insert( out_sorted, index, simpleness );
146 0 : if ( 0 != insert_error )
147 : {
148 0 : U8_LOG_WARNING( "not all relationship label-boxes are layouted" );
149 : }
150 : }
151 : }
152 :
153 0 : U8_TRACE_END();
154 0 : }
155 :
156 0 : void pencil_rel_label_layouter_private_propose_solutions ( pencil_rel_label_layouter_t *this_,
157 : layout_relationship_t *current_relation,
158 : PangoLayout *font_layout,
159 : uint32_t solutions_max,
160 : geometry_rectangle_t out_solutions[],
161 : uint32_t *out_solutions_count)
162 : {
163 0 : U8_TRACE_BEGIN();
164 0 : assert( NULL != current_relation );
165 0 : assert( NULL != font_layout );
166 0 : assert( NULL != out_solutions );
167 0 : assert( NULL != out_solutions_count );
168 :
169 0 : const data_relationship_t *the_relationship = layout_relationship_get_data_const( current_relation );
170 : {
171 : /* determine label dimensions */
172 0 : const geometry_dimensions_t label_dim_proposal = {
173 0 : .width = 15.0 * pencil_size_get_standard_font_size( (*this_).pencil_size ),
174 0 : .height = pencil_size_get_standard_font_size( (*this_).pencil_size )
175 : };
176 : geometry_dimensions_t label_dim;
177 0 : draw_relationship_label_get_type_and_name_dimensions( &((*this_).draw_relationship_label),
178 : the_relationship,
179 : (*this_).profile,
180 : &label_dim_proposal,
181 : (*this_).pencil_size,
182 : font_layout,
183 : &label_dim
184 : );
185 0 : const double text_width = geometry_dimensions_get_width( &label_dim );
186 0 : const double text_height = geometry_dimensions_get_height( &label_dim );
187 :
188 : /* get layout data */
189 0 : const double gap = pencil_size_get_standard_object_border( (*this_).pencil_size );
190 0 : const double object_dist = pencil_size_get_preferred_object_distance( (*this_).pencil_size );
191 :
192 : /* get connector data */
193 0 : const geometry_connector_t *const shape = layout_relationship_get_shape_const ( current_relation );
194 0 : const double source_end_x = geometry_connector_get_source_end_x ( shape );
195 0 : const double source_end_y = geometry_connector_get_source_end_y ( shape );
196 0 : const double main_line_source_x = geometry_connector_get_main_line_source_x ( shape );
197 0 : const double main_line_source_y = geometry_connector_get_main_line_source_y ( shape );
198 0 : const double main_line_destination_x = geometry_connector_get_main_line_destination_x ( shape );
199 0 : const double main_line_destination_y = geometry_connector_get_main_line_destination_y ( shape );
200 0 : const double destination_end_x = geometry_connector_get_destination_end_x ( shape );
201 0 : const double destination_end_y = geometry_connector_get_destination_end_y ( shape );
202 : geometry_point_t src_end;
203 : geometry_point_t main_src;
204 : geometry_point_t main_dst;
205 : geometry_point_t dst_end;
206 0 : geometry_point_init ( &src_end, source_end_x, source_end_y );
207 0 : geometry_point_init ( &main_src, main_line_source_x, main_line_source_y );
208 0 : geometry_point_init ( &main_dst, main_line_destination_x, main_line_destination_y );
209 0 : geometry_point_init ( &dst_end, destination_end_x, destination_end_y );
210 0 : const geometry_direction_t src_dir = geometry_point_get_direction ( &src_end, &main_src );
211 0 : const geometry_direction_t dst_dir = geometry_point_get_direction ( &main_dst, &dst_end );
212 : geometry_rectangle_t main_line_rect;
213 0 : geometry_rectangle_init_by_corners ( &main_line_rect, main_line_source_x, main_line_source_y, main_line_destination_x, main_line_destination_y );
214 :
215 : /* propose solutions */
216 0 : assert( solutions_max >= 16 );
217 0 : uint32_t solution_idx = 0;
218 :
219 : /* there are 0..2 solutions at the src line segment */
220 0 : if ( geometry_point_calc_chess_distance( &src_end, &main_src ) > object_dist )
221 : {
222 : /* this is a noteworthy line segment */
223 0 : if ( ( src_dir == GEOMETRY_DIRECTION_UP ) || ( src_dir == GEOMETRY_DIRECTION_DOWN ) )
224 : {
225 : /* right */
226 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
227 : main_line_source_x + gap,
228 0 : (source_end_y + main_line_source_y - text_height) / 2.0,
229 : text_width,
230 : text_height
231 : );
232 0 : solution_idx ++;
233 :
234 : /* left */
235 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
236 0 : main_line_source_x - text_width - gap,
237 0 : (source_end_y + main_line_source_y - text_height) / 2.0,
238 : text_width,
239 : text_height
240 : );
241 0 : solution_idx ++;
242 : }
243 : else
244 : {
245 : /* down */
246 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
247 0 : (source_end_x + main_line_source_x - text_width) / 2.0,
248 : main_line_source_y + gap,
249 : text_width,
250 : text_height
251 : );
252 0 : solution_idx ++;
253 :
254 : /* up */
255 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
256 0 : (source_end_x + main_line_source_x - text_width) / 2.0,
257 0 : main_line_source_y - text_height - gap,
258 : text_width,
259 : text_height
260 : );
261 0 : solution_idx ++;
262 :
263 : }
264 : }
265 :
266 : /* there are 4 solutions at the main line segment */
267 : {
268 : /* left */
269 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
270 0 : geometry_rectangle_get_left(&main_line_rect) - text_width - gap,
271 0 : (main_line_source_y + main_line_destination_y - text_height) / 2.0,
272 : text_width,
273 : text_height
274 : );
275 0 : solution_idx ++;
276 :
277 : /* right */
278 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
279 0 : geometry_rectangle_get_right(&main_line_rect) + gap,
280 0 : (main_line_source_y + main_line_destination_y - text_height) / 2.0,
281 : text_width,
282 : text_height
283 : );
284 0 : solution_idx ++;
285 :
286 : /* up */
287 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
288 0 : (main_line_source_x + main_line_destination_x - text_width) / 2.0,
289 0 : geometry_rectangle_get_top(&main_line_rect) - text_height - gap,
290 : text_width,
291 : text_height
292 : );
293 0 : solution_idx ++;
294 :
295 : /* down */
296 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
297 0 : (main_line_source_x + main_line_destination_x - text_width) / 2.0,
298 0 : geometry_rectangle_get_bottom(&main_line_rect) + gap,
299 : text_width,
300 : text_height
301 : );
302 0 : solution_idx ++;
303 : }
304 :
305 : /* there are 0..2 solutions at the dst line segment */
306 0 : if ( geometry_point_calc_chess_distance( &main_dst, &dst_end ) > object_dist )
307 : {
308 : /* this is a noteworthy line segment */
309 0 : if ( ( dst_dir == GEOMETRY_DIRECTION_UP ) || ( dst_dir == GEOMETRY_DIRECTION_DOWN ) )
310 : {
311 : /* right */
312 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
313 : main_line_destination_x + gap,
314 0 : (destination_end_y + main_line_destination_y - text_height) / 2.0,
315 : text_width,
316 : text_height
317 : );
318 0 : solution_idx ++;
319 :
320 : /* left */
321 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
322 0 : main_line_destination_x - text_width - gap,
323 0 : (destination_end_y + main_line_destination_y - text_height) / 2.0,
324 : text_width,
325 : text_height
326 : );
327 0 : solution_idx ++;
328 : }
329 : else
330 : {
331 : /* down */
332 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
333 0 : (destination_end_x + main_line_destination_x - text_width) / 2.0,
334 : main_line_destination_y + gap,
335 : text_width,
336 : text_height
337 : );
338 0 : solution_idx ++;
339 :
340 : /* up */
341 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
342 0 : (destination_end_x + main_line_destination_x - text_width) / 2.0,
343 0 : main_line_destination_y - text_height - gap,
344 : text_width,
345 : text_height
346 : );
347 0 : solution_idx ++;
348 : }
349 : }
350 :
351 : /* 0..4 at main line source if source end line exists */
352 0 : if ( src_dir != GEOMETRY_DIRECTION_CENTER )
353 : {
354 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
355 0 : main_line_source_x - text_width - gap,
356 0 : main_line_source_y - text_height - gap,
357 : text_width,
358 : text_height
359 : );
360 0 : solution_idx ++;
361 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
362 0 : main_line_source_x - text_width - gap,
363 : main_line_source_y + gap,
364 : text_width,
365 : text_height
366 : );
367 0 : solution_idx ++;
368 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
369 : main_line_source_x + gap,
370 0 : main_line_source_y - text_height - gap,
371 : text_width,
372 : text_height
373 : );
374 0 : solution_idx ++;
375 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
376 : main_line_source_x + gap,
377 : main_line_source_y + gap,
378 : text_width,
379 : text_height
380 : );
381 0 : solution_idx ++;
382 : }
383 :
384 : /* 0..4 at main line destination if destination end line exists */
385 0 : if ( dst_dir != GEOMETRY_DIRECTION_CENTER )
386 : {
387 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
388 0 : main_line_destination_x - text_width - gap,
389 0 : main_line_destination_y - text_height - gap,
390 : text_width,
391 : text_height
392 : );
393 0 : solution_idx ++;
394 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
395 0 : main_line_destination_x - text_width - gap,
396 : main_line_destination_y + gap,
397 : text_width,
398 : text_height
399 : );
400 0 : solution_idx ++;
401 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
402 : main_line_destination_x + gap,
403 0 : main_line_destination_y - text_height - gap,
404 : text_width,
405 : text_height
406 : );
407 0 : solution_idx ++;
408 0 : geometry_rectangle_init( &(out_solutions[solution_idx]),
409 : main_line_destination_x + gap,
410 : main_line_destination_y + gap,
411 : text_width,
412 : text_height
413 : );
414 0 : solution_idx ++;
415 : }
416 :
417 0 : assert( solution_idx > 0 );
418 0 : assert( solution_idx <= solutions_max );
419 0 : *out_solutions_count = solution_idx;
420 : }
421 :
422 0 : U8_TRACE_END();
423 0 : }
424 :
425 :
426 : /*
427 : Copyright 2019-2024 Andreas Warnke
428 :
429 : Licensed under the Apache License, Version 2.0 (the "License");
430 : you may not use this file except in compliance with the License.
431 : You may obtain a copy of the License at
432 :
433 : http://www.apache.org/licenses/LICENSE-2.0
434 :
435 : Unless required by applicable law or agreed to in writing, software
436 : distributed under the License is distributed on an "AS IS" BASIS,
437 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
438 : See the License for the specific language governing permissions and
439 : limitations under the License.
440 : */
|