Line data Source code
1 : /* File: pencil_feat_label_layouter.c; Copyright and License: see below */
2 :
3 : #include "pencil_feat_label_layouter.h"
4 : #include "layout/layout_feature_iter.h"
5 : #include "u8/u8_trace.h"
6 : #include "utf8stringbuf/utf8string.h"
7 :
8 0 : void pencil_feat_label_layouter_init( pencil_feat_label_layouter_t *this_,
9 : layout_visible_set_t *layout_data,
10 : const data_profile_part_t *profile,
11 : const pencil_size_t *pencil_size )
12 : {
13 0 : U8_TRACE_BEGIN();
14 0 : assert( NULL != layout_data );
15 0 : assert( NULL != profile );
16 0 : assert( NULL != pencil_size );
17 :
18 0 : (*this_).layout_data = layout_data;
19 0 : (*this_).profile = profile;
20 0 : (*this_).pencil_size = pencil_size;
21 0 : draw_feature_label_init( &((*this_).draw_feature_label) );
22 0 : pencil_floating_label_layouter_init_void( &((*this_).label_floater) );
23 :
24 0 : U8_TRACE_END();
25 0 : }
26 :
27 0 : void pencil_feat_label_layouter_reinit( pencil_feat_label_layouter_t *this_,
28 : layout_visible_set_t *layout_data,
29 : const data_profile_part_t *profile,
30 : const pencil_size_t *pencil_size )
31 : {
32 0 : U8_TRACE_BEGIN();
33 0 : assert( NULL != layout_data );
34 0 : assert( NULL != profile );
35 0 : assert( NULL != pencil_size );
36 :
37 0 : (*this_).layout_data = layout_data;
38 0 : (*this_).profile = profile;
39 0 : (*this_).pencil_size = pencil_size;
40 0 : pencil_floating_label_layouter_init_void( &((*this_).label_floater) );
41 :
42 0 : U8_TRACE_END();
43 0 : }
44 :
45 0 : void pencil_feat_label_layouter_destroy( pencil_feat_label_layouter_t *this_ )
46 : {
47 0 : U8_TRACE_BEGIN();
48 :
49 0 : pencil_floating_label_layouter_destroy ( &((*this_).label_floater) );
50 0 : draw_feature_label_destroy( &((*this_).draw_feature_label) );
51 :
52 0 : U8_TRACE_END();
53 0 : }
54 :
55 0 : void pencil_feat_label_layouter_do_layout ( pencil_feat_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_FEATURES );
59 0 : assert( NULL != font_layout );
60 :
61 0 : pencil_floating_label_layouter_reinit( &((*this_).label_floater),
62 : (*this_).layout_data,
63 : (*this_).profile,
64 : (*this_).pencil_size,
65 : font_layout
66 : );
67 :
68 : universal_array_index_sorter_t sorted;
69 0 : universal_array_index_sorter_init( &sorted );
70 :
71 : /* sort the features by their label-box layouting needs, drop invisible relations */
72 0 : pencil_feat_label_layouter_private_propose_processing_order ( this_, &sorted );
73 :
74 : /* layout the features label-boxes */
75 : layout_feature_iter_t feature_iterator;
76 0 : layout_feature_iter_init( &feature_iterator, (*this_).layout_data, &sorted );
77 0 : while ( layout_feature_iter_has_next( &feature_iterator ) )
78 : {
79 : /* determine pointer to feature */
80 0 : layout_feature_t *const current_feature = layout_feature_iter_next_ptr( &feature_iterator );
81 0 : geometry_point_t feature_middle = layout_feature_get_symbol_middle ( current_feature );
82 :
83 : /* declaration of list of options */
84 0 : uint32_t solutions_count = 0;
85 : static const uint32_t SOLUTIONS_MAX = 8;
86 : geometry_rectangle_t solution[8];
87 :
88 : /* propose options */
89 0 : pencil_feat_label_layouter_private_propose_solutions( this_,
90 : current_feature,
91 : font_layout,
92 : SOLUTIONS_MAX,
93 : solution,
94 : &solutions_count
95 : );
96 :
97 : /* select best option */
98 : uint32_t index_of_best;
99 0 : if ( 1 == solutions_count )
100 : {
101 0 : index_of_best = 0;
102 : }
103 : else
104 : {
105 0 : pencil_floating_label_layouter_select_solution( &((*this_).label_floater),
106 : feature_middle,
107 : solutions_count,
108 : solution,
109 : &index_of_best
110 : );
111 : }
112 :
113 : /* store best option to (*this_).layout_data */
114 0 : layout_feature_set_label_box( current_feature, &(solution[index_of_best]) );
115 : }
116 :
117 0 : layout_feature_iter_destroy( &feature_iterator );
118 0 : universal_array_index_sorter_destroy( &sorted );
119 :
120 0 : pencil_floating_label_layouter_reinit_void( &((*this_).label_floater) );
121 :
122 0 : U8_TRACE_END();
123 0 : }
124 :
125 0 : void pencil_feat_label_layouter_private_propose_processing_order ( pencil_feat_label_layouter_t *this_, universal_array_index_sorter_t *out_sorted )
126 : {
127 0 : U8_TRACE_BEGIN();
128 0 : assert( NULL != out_sorted );
129 :
130 : /* sort the features by their label-box: the less simple, the earlier it shall be processed */
131 : const uint32_t count_features
132 0 : = layout_visible_set_get_feature_count( (*this_).layout_data );
133 0 : for ( uint32_t index = 0; index < count_features; index ++ )
134 : {
135 : const layout_feature_t *const current_feature
136 0 : = layout_visible_set_get_feature_ptr( (*this_).layout_data, index );
137 0 : const data_feature_t *const feature_data = layout_feature_get_data_const( current_feature );
138 0 : assert( NULL != feature_data );
139 0 : const data_feature_type_t current_type = data_feature_get_main_type( feature_data );
140 :
141 0 : int64_t simpleness = 0;
142 :
143 : /* determine simpleness by length of label */
144 0 : simpleness -= utf8string_get_length( data_feature_get_key_const( feature_data ) );
145 0 : simpleness -= utf8string_get_length( data_feature_get_value_const( feature_data ) );
146 :
147 : /* do the properties and operations first, they have a fixed positiion given by the classifier */
148 0 : if ( ! data_feature_type_outside_compartment( current_type ) )
149 : {
150 0 : simpleness = -100000;
151 : }
152 :
153 : /* insert relation to sorted array - except for lifelines which have no label */
154 0 : if ( DATA_FEATURE_TYPE_LIFELINE != current_type )
155 : {
156 : const int insert_error
157 0 : = universal_array_index_sorter_insert( out_sorted, index, simpleness );
158 0 : if ( 0 != insert_error )
159 : {
160 0 : U8_LOG_WARNING( "not all relationship label-boxes are layouted" );
161 : }
162 : }
163 : }
164 :
165 0 : U8_TRACE_END();
166 0 : }
167 :
168 0 : void pencil_feat_label_layouter_private_propose_solutions ( pencil_feat_label_layouter_t *this_,
169 : layout_feature_t *current_feature,
170 : PangoLayout *font_layout,
171 : uint32_t solutions_max,
172 : geometry_rectangle_t out_solutions[],
173 : uint32_t *out_solutions_count )
174 : {
175 0 : U8_TRACE_BEGIN();
176 0 : assert( NULL != current_feature );
177 0 : assert( NULL != font_layout );
178 0 : assert( NULL != out_solutions );
179 0 : assert( NULL != out_solutions_count );
180 :
181 0 : const data_feature_t *feature_data = layout_feature_get_data_const ( current_feature );
182 0 : assert( NULL != feature_data );
183 0 : const data_feature_type_t current_type = data_feature_get_main_type( feature_data );
184 :
185 0 : if ( data_feature_type_inside_compartment( current_type ) )
186 : {
187 : /* the label-box is already calculated by pencil_feature_layouter_do_layout() */
188 : /* which is called before pencil_feat_label_layouter_do_layout() */
189 0 : assert( solutions_max >= 1 );
190 0 : geometry_rectangle_copy( &(out_solutions[0]), layout_feature_get_label_box_const ( current_feature ) );
191 0 : *out_solutions_count = 1;
192 : }
193 : else
194 : {
195 : /* key and value dimensions */
196 0 : const geometry_dimensions_t label_dim_proposal = {
197 0 : .width = 25.0 * pencil_size_get_standard_font_size( (*this_).pencil_size ),
198 0 : .height = pencil_size_get_standard_font_size( (*this_).pencil_size )
199 : };
200 : geometry_dimensions_t preferred_label_dim;
201 0 : draw_feature_label_get_key_and_value_dimensions( &((*this_).draw_feature_label),
202 : feature_data,
203 : (*this_).profile,
204 : &label_dim_proposal,
205 : (*this_).pencil_size,
206 : font_layout,
207 : &preferred_label_dim
208 : );
209 :
210 0 : const double small_gap = 0.000000001;
211 0 : const geometry_rectangle_t *const bounds = layout_feature_get_symbol_box_const ( current_feature );
212 0 : const double left = geometry_rectangle_get_left( bounds );
213 0 : const double top = geometry_rectangle_get_top( bounds );
214 0 : const double bottom = geometry_rectangle_get_bottom( bounds );
215 0 : const double right = geometry_rectangle_get_right( bounds );
216 :
217 0 : assert( solutions_max >= 8 );
218 : /* top-left */
219 : {
220 : geometry_anchor_t anchor_0;
221 : geometry_anchor_t anchor_1;
222 :
223 0 : geometry_anchor_init( &anchor_0,
224 : left - small_gap,
225 : top - small_gap,
226 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
227 : GEOMETRY_V_ALIGN_CENTER
228 : );
229 0 : geometry_anchor_init( &anchor_1,
230 : left - small_gap,
231 : top - small_gap,
232 : GEOMETRY_H_ALIGN_CENTER,
233 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom side of the label */
234 : );
235 :
236 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
237 : &anchor_0,
238 : &preferred_label_dim,
239 : &((*this_).draw_feature_label),
240 : feature_data,
241 : &(out_solutions[0])
242 : );
243 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
244 : &anchor_1,
245 : &preferred_label_dim,
246 : &((*this_).draw_feature_label),
247 : feature_data,
248 : &(out_solutions[1])
249 : );
250 : }
251 :
252 : /* top-right */
253 : {
254 : geometry_anchor_t anchor_2;
255 : geometry_anchor_t anchor_3;
256 :
257 0 : geometry_anchor_init( &anchor_2,
258 : right + small_gap,
259 : top - small_gap,
260 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
261 : GEOMETRY_V_ALIGN_CENTER
262 : );
263 0 : geometry_anchor_init( &anchor_3,
264 : right + small_gap,
265 : top - small_gap,
266 : GEOMETRY_H_ALIGN_CENTER,
267 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom side of the label */
268 : );
269 :
270 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
271 : &anchor_2,
272 : &preferred_label_dim,
273 : &((*this_).draw_feature_label),
274 : feature_data,
275 : &(out_solutions[2])
276 : );
277 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
278 : &anchor_3,
279 : &preferred_label_dim,
280 : &((*this_).draw_feature_label),
281 : feature_data,
282 : &(out_solutions[3])
283 : );
284 : }
285 :
286 : /* bottom-left */
287 : {
288 : geometry_anchor_t anchor_4;
289 : geometry_anchor_t anchor_5;
290 :
291 0 : geometry_anchor_init( &anchor_4,
292 : left - small_gap,
293 : bottom + small_gap,
294 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
295 : GEOMETRY_V_ALIGN_CENTER
296 : );
297 0 : geometry_anchor_init( &anchor_5,
298 : left - small_gap,
299 : bottom + small_gap,
300 : GEOMETRY_H_ALIGN_CENTER,
301 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top side of the label */
302 : );
303 :
304 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
305 : &anchor_4,
306 : &preferred_label_dim,
307 : &((*this_).draw_feature_label),
308 : feature_data,
309 : &(out_solutions[4])
310 : );
311 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
312 : &anchor_5,
313 : &preferred_label_dim,
314 : &((*this_).draw_feature_label),
315 : feature_data,
316 : &(out_solutions[5])
317 : );
318 : }
319 :
320 : /* bottom-right */
321 : {
322 : geometry_anchor_t anchor_6;
323 : geometry_anchor_t anchor_7;
324 :
325 0 : geometry_anchor_init( &anchor_6,
326 : right + small_gap,
327 : bottom + small_gap,
328 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
329 : GEOMETRY_V_ALIGN_CENTER
330 : );
331 0 : geometry_anchor_init( &anchor_7,
332 : right + small_gap,
333 : bottom + small_gap,
334 : GEOMETRY_H_ALIGN_CENTER,
335 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top side of the label */
336 : );
337 :
338 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
339 : &anchor_6,
340 : &preferred_label_dim,
341 : &((*this_).draw_feature_label),
342 : feature_data,
343 : &(out_solutions[6])
344 : );
345 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
346 : &anchor_7,
347 : &preferred_label_dim,
348 : &((*this_).draw_feature_label),
349 : feature_data,
350 : &(out_solutions[7])
351 : );
352 : }
353 :
354 0 : *out_solutions_count = 8;
355 : }
356 :
357 0 : U8_TRACE_END();
358 0 : }
359 :
360 :
361 : /*
362 : * Copyright 2019-2025 Andreas Warnke
363 : *
364 : * Licensed under the Apache License, Version 2.0 (the "License");
365 : * you may not use this file except in compliance with the License.
366 : * You may obtain a copy of the License at
367 : *
368 : * http://www.apache.org/licenses/LICENSE-2.0
369 : *
370 : * Unless required by applicable law or agreed to in writing, software
371 : * distributed under the License is distributed on an "AS IS" BASIS,
372 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
373 : * See the License for the specific language governing permissions and
374 : * limitations under the License.
375 : */
|