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 : /* get layout data */
211 0 : const double border_gap = pencil_size_get_standard_object_border( (*this_).pencil_size );
212 0 : const geometry_rectangle_t *const bounds = layout_feature_get_symbol_box_const ( current_feature );
213 0 : const double left = geometry_rectangle_get_left( bounds );
214 0 : const double top = geometry_rectangle_get_top( bounds );
215 0 : const double bottom = geometry_rectangle_get_bottom( bounds );
216 0 : const double right = geometry_rectangle_get_right( bounds );
217 :
218 0 : assert( solutions_max >= 8 );
219 : /* top-left */
220 : {
221 : geometry_anchor_t anchor_0;
222 : geometry_anchor_t anchor_1;
223 :
224 0 : geometry_anchor_init( &anchor_0,
225 : left - border_gap,
226 : top - border_gap,
227 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
228 : GEOMETRY_V_ALIGN_CENTER
229 : );
230 0 : geometry_anchor_init( &anchor_1,
231 : left - border_gap,
232 : top - border_gap,
233 : GEOMETRY_H_ALIGN_CENTER,
234 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom side of the label */
235 : );
236 :
237 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
238 : &anchor_0,
239 : &preferred_label_dim,
240 : &((*this_).draw_feature_label),
241 : feature_data,
242 : &(out_solutions[0])
243 : );
244 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
245 : &anchor_1,
246 : &preferred_label_dim,
247 : &((*this_).draw_feature_label),
248 : feature_data,
249 : &(out_solutions[1])
250 : );
251 : }
252 :
253 : /* top-right */
254 : {
255 : geometry_anchor_t anchor_2;
256 : geometry_anchor_t anchor_3;
257 :
258 0 : geometry_anchor_init( &anchor_2,
259 : right + border_gap,
260 : top - border_gap,
261 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
262 : GEOMETRY_V_ALIGN_CENTER
263 : );
264 0 : geometry_anchor_init( &anchor_3,
265 : right + border_gap,
266 : top - border_gap,
267 : GEOMETRY_H_ALIGN_CENTER,
268 : GEOMETRY_V_ALIGN_BOTTOM /* the reference point is the bottom side of the label */
269 : );
270 :
271 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
272 : &anchor_2,
273 : &preferred_label_dim,
274 : &((*this_).draw_feature_label),
275 : feature_data,
276 : &(out_solutions[2])
277 : );
278 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
279 : &anchor_3,
280 : &preferred_label_dim,
281 : &((*this_).draw_feature_label),
282 : feature_data,
283 : &(out_solutions[3])
284 : );
285 : }
286 :
287 : /* bottom-left */
288 : {
289 : geometry_anchor_t anchor_4;
290 : geometry_anchor_t anchor_5;
291 :
292 0 : geometry_anchor_init( &anchor_4,
293 : left - border_gap,
294 : bottom + border_gap,
295 : GEOMETRY_H_ALIGN_RIGHT, /* the reference point is the right side of the label */
296 : GEOMETRY_V_ALIGN_CENTER
297 : );
298 0 : geometry_anchor_init( &anchor_5,
299 : left - border_gap,
300 : bottom + border_gap,
301 : GEOMETRY_H_ALIGN_CENTER,
302 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top side of the label */
303 : );
304 :
305 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
306 : &anchor_4,
307 : &preferred_label_dim,
308 : &((*this_).draw_feature_label),
309 : feature_data,
310 : &(out_solutions[4])
311 : );
312 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
313 : &anchor_5,
314 : &preferred_label_dim,
315 : &((*this_).draw_feature_label),
316 : feature_data,
317 : &(out_solutions[5])
318 : );
319 : }
320 :
321 : /* bottom-right */
322 : {
323 : geometry_anchor_t anchor_6;
324 : geometry_anchor_t anchor_7;
325 :
326 0 : geometry_anchor_init( &anchor_6,
327 : right + border_gap,
328 : bottom + border_gap,
329 : GEOMETRY_H_ALIGN_LEFT, /* the reference point is the left side of the label */
330 : GEOMETRY_V_ALIGN_CENTER
331 : );
332 0 : geometry_anchor_init( &anchor_7,
333 : right + border_gap,
334 : bottom + border_gap,
335 : GEOMETRY_H_ALIGN_CENTER,
336 : GEOMETRY_V_ALIGN_TOP /* the reference point is the top side of the label */
337 : );
338 :
339 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
340 : &anchor_6,
341 : &preferred_label_dim,
342 : &((*this_).draw_feature_label),
343 : feature_data,
344 : &(out_solutions[6])
345 : );
346 0 : pencil_floating_label_layouter_propose_solution_feat( &((*this_).label_floater),
347 : &anchor_7,
348 : &preferred_label_dim,
349 : &((*this_).draw_feature_label),
350 : feature_data,
351 : &(out_solutions[7])
352 : );
353 : }
354 :
355 0 : *out_solutions_count = 8;
356 : }
357 :
358 0 : U8_TRACE_END();
359 0 : }
360 :
361 :
362 : /*
363 : * Copyright 2019-2026 Andreas Warnke
364 : *
365 : * Licensed under the Apache License, Version 2.0 (the "License");
366 : * you may not use this file except in compliance with the License.
367 : * You may obtain a copy of the License at
368 : *
369 : * http://www.apache.org/licenses/LICENSE-2.0
370 : *
371 : * Unless required by applicable law or agreed to in writing, software
372 : * distributed under the License is distributed on an "AS IS" BASIS,
373 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
374 : * See the License for the specific language governing permissions and
375 : * limitations under the License.
376 : */
|