Line data Source code
1 : /* File: pencil_layouter.c; Copyright and License: see below */
2 :
3 : #include "pencil_layouter.h"
4 : #include "u8/u8_trace.h"
5 : #include <pango/pangocairo.h>
6 : #include <stdio.h>
7 : #include <stdlib.h>
8 : #include <math.h>
9 :
10 0 : void pencil_layouter_init( pencil_layouter_t *this_,
11 : const data_visible_set_t *input_data,
12 : const data_profile_part_t *profile )
13 : {
14 0 : U8_TRACE_BEGIN();
15 0 : assert( NULL != input_data );
16 0 : assert( NULL != profile );
17 :
18 0 : pencil_size_init_empty( &((*this_).pencil_size) );
19 0 : geometry_non_linear_scale_init( &((*this_).x_scale), 0.0, 1.0 );
20 0 : geometry_non_linear_scale_init( &((*this_).y_scale), 0.0, 1.0 );
21 0 : geometry_dimensions_init_empty( &((*this_).default_classifier_size) );
22 0 : data_rules_init( &((*this_).rules) );
23 :
24 : /* initialize the layout data objects */
25 0 : pencil_layout_data_init( &((*this_).layout_data), input_data );
26 0 : (*this_).profile = profile;
27 :
28 0 : pencil_diagram_painter_init( &((*this_).diagram_painter) );
29 :
30 0 : pencil_feature_layouter_init( &((*this_).feature_layouter),
31 : &((*this_).layout_data),
32 : profile,
33 0 : &((*this_).pencil_size)
34 : );
35 0 : pencil_feat_label_layouter_init( &((*this_).feature_label_layouter),
36 : &((*this_).layout_data),
37 : profile,
38 0 : &((*this_).pencil_size)
39 : );
40 0 : pencil_classifier_2d_layouter_init( &((*this_).pencil_classifier_2d_layouter),
41 : &((*this_).layout_data),
42 : profile,
43 0 : &((*this_).pencil_size),
44 : &((*this_).default_classifier_size),
45 : &((*this_).x_scale),
46 : &((*this_).y_scale),
47 : &((*this_).feature_layouter)
48 : );
49 0 : pencil_classifier_1d_layouter_init( &((*this_).pencil_classifier_1d_layouter),
50 : &((*this_).layout_data),
51 : profile,
52 0 : &((*this_).pencil_size)
53 : );
54 0 : pencil_relationship_2d_layouter_init( &((*this_).pencil_relationship_2d_layouter),
55 : &((*this_).layout_data),
56 : profile,
57 0 : &((*this_).pencil_size)
58 : );
59 0 : pencil_relationship_1d_layouter_init( &((*this_).pencil_relationship_1d_layouter),
60 : &((*this_).layout_data),
61 : profile,
62 0 : &((*this_).pencil_size)
63 : );
64 0 : pencil_rel_label_layouter_init( &((*this_).relationship_label_layouter),
65 : &((*this_).layout_data),
66 : profile,
67 0 : &((*this_).pencil_size)
68 : );
69 0 : U8_TRACE_END();
70 0 : }
71 :
72 0 : void pencil_layouter_reinit( pencil_layouter_t *this_,
73 : const data_visible_set_t *input_data,
74 : const data_profile_part_t *profile )
75 : {
76 0 : U8_TRACE_BEGIN();
77 0 : assert( NULL != input_data );
78 0 : assert( NULL != profile );
79 0 : pencil_layouter_destroy( this_ );
80 0 : pencil_layouter_init( this_, input_data, profile );
81 0 : U8_TRACE_END();
82 0 : }
83 :
84 0 : void pencil_layouter_destroy( pencil_layouter_t *this_ )
85 : {
86 0 : U8_TRACE_BEGIN();
87 :
88 0 : pencil_rel_label_layouter_destroy( &((*this_).relationship_label_layouter) );
89 0 : pencil_relationship_1d_layouter_destroy( &((*this_).pencil_relationship_1d_layouter) );
90 0 : pencil_relationship_2d_layouter_destroy( &((*this_).pencil_relationship_2d_layouter) );
91 0 : pencil_classifier_1d_layouter_destroy( &((*this_).pencil_classifier_1d_layouter) );
92 0 : pencil_classifier_2d_layouter_destroy( &((*this_).pencil_classifier_2d_layouter) );
93 0 : pencil_feat_label_layouter_destroy( &((*this_).feature_label_layouter) );
94 0 : pencil_feature_layouter_destroy( &((*this_).feature_layouter) );
95 :
96 0 : pencil_diagram_painter_destroy( &((*this_).diagram_painter) );
97 :
98 0 : pencil_size_destroy( &((*this_).pencil_size) );
99 0 : geometry_non_linear_scale_destroy( &((*this_).x_scale) );
100 0 : geometry_non_linear_scale_destroy( &((*this_).y_scale) );
101 0 : geometry_dimensions_destroy( &((*this_).default_classifier_size) );
102 0 : data_rules_destroy( &((*this_).rules) );
103 :
104 0 : pencil_layout_data_destroy( &((*this_).layout_data) );
105 :
106 0 : U8_TRACE_END();
107 0 : }
108 :
109 0 : void pencil_layouter_define_grid ( pencil_layouter_t *this_,
110 : geometry_rectangle_t diagram_bounds,
111 : PangoLayout *font_layout )
112 : {
113 0 : U8_TRACE_BEGIN();
114 0 : geometry_rectangle_trace( &diagram_bounds );
115 :
116 : /* get the diagram data */
117 : layout_diagram_t *the_diagram;
118 0 : the_diagram = pencil_layout_data_get_diagram_ptr( &((*this_).layout_data) );
119 : const data_diagram_t *const diagram_data
120 0 : = layout_diagram_get_data_const ( the_diagram );
121 :
122 : /* calculate the pencil-sizes and the drawing rectangle */
123 0 : const double width = geometry_rectangle_get_width( &diagram_bounds );
124 0 : const double height = geometry_rectangle_get_height( &diagram_bounds );
125 0 : pencil_size_reinit( &((*this_).pencil_size), width, height );
126 :
127 0 : pencil_diagram_painter_do_layout( &((*this_).diagram_painter),
128 : diagram_data,
129 : &diagram_bounds,
130 : (*this_).profile,
131 0 : &((*this_).pencil_size),
132 : font_layout,
133 : the_diagram
134 : );
135 : const geometry_rectangle_t *const diagram_draw_area
136 0 : = layout_diagram_get_draw_area_const( the_diagram );
137 :
138 : /* calculate the axis scales */
139 0 : geometry_rectangle_trace( diagram_draw_area );
140 0 : const double draw_left = geometry_rectangle_get_left( diagram_draw_area );
141 0 : const double draw_top = geometry_rectangle_get_top( diagram_draw_area );
142 0 : const double draw_right = geometry_rectangle_get_right( diagram_draw_area );
143 0 : const double draw_bottom = geometry_rectangle_get_bottom( diagram_draw_area );
144 0 : geometry_non_linear_scale_reinit( &((*this_).x_scale), draw_left, draw_right );
145 0 : geometry_non_linear_scale_reinit( &((*this_).y_scale), draw_top, draw_bottom );
146 :
147 : /* iterate over all classifiers */
148 : const uint32_t count
149 0 : = pencil_layout_data_get_visible_classifier_count ( &((*this_).layout_data) );
150 0 : for ( uint32_t index = 0; index < count; index ++ )
151 : {
152 : const layout_visible_classifier_t *const visible_classifier
153 0 : = pencil_layout_data_get_visible_classifier_ptr ( &((*this_).layout_data), index );
154 : const data_classifier_t *const classifier_data
155 0 : = layout_visible_classifier_get_classifier_const( visible_classifier );
156 : const uint32_t visible_descendants
157 0 : = pencil_layout_data_count_descendants( &((*this_).layout_data), visible_classifier );
158 :
159 : /* adjust the non-linear scales for this classifier (if no contained descendants) */
160 0 : if ( 0 == visible_descendants )
161 : {
162 0 : geometry_non_linear_scale_add_order ( &((*this_).x_scale), data_classifier_get_x_order( classifier_data ) );
163 0 : geometry_non_linear_scale_add_order ( &((*this_).y_scale), data_classifier_get_y_order( classifier_data ) );
164 : }
165 : }
166 :
167 0 : U8_TRACE_END();
168 0 : }
169 :
170 0 : void pencil_layouter_layout_elements ( pencil_layouter_t *this_, PangoLayout *font_layout )
171 : {
172 0 : U8_TRACE_BEGIN();
173 0 : assert( font_layout != NULL );
174 :
175 : /* get the diagram data */
176 0 : const layout_diagram_t *const the_diagram = pencil_layout_data_get_diagram_ptr( &((*this_).layout_data) );
177 0 : const data_diagram_t *const diagram_data = layout_diagram_get_data_const ( the_diagram );
178 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type ( diagram_data );
179 :
180 : /* adjust the default classifier rectangle */
181 0 : pencil_layouter_private_propose_default_classifier_size( this_ );
182 :
183 : /* store the classifier bounds into input_data_layouter_t */
184 0 : if ( DATA_DIAGRAM_TYPE_LIST == diag_type )
185 : {
186 : /* calculate the classifier shapes */
187 0 : pencil_classifier_1d_layouter_layout_for_list( &((*this_).pencil_classifier_1d_layouter), font_layout );
188 :
189 : /* calculate the feature shapes */
190 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
191 :
192 : /* hide relationships in simple list and box diagrams */
193 0 : pencil_relationship_2d_layouter_layout_void( &((*this_).pencil_relationship_2d_layouter) );
194 :
195 : /* layout labels of features */
196 0 : pencil_feat_label_layouter_do_layout( &((*this_).feature_label_layouter), font_layout );
197 : }
198 0 : else if ( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )
199 : {
200 : /* calculate the classifier shapes */
201 0 : pencil_classifier_1d_layouter_layout_for_sequence( &((*this_).pencil_classifier_1d_layouter), font_layout );
202 :
203 : /* calculate the feature shapes */
204 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
205 :
206 : /* calculate the relationship shapes for a sequence diagram */
207 0 : pencil_relationship_1d_layouter_layout_for_sequence( &((*this_).pencil_relationship_1d_layouter) );
208 :
209 : /* layout labels of relationships */
210 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
211 : }
212 0 : else if ( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )
213 : {
214 : /* calculate the classifier shapes */
215 0 : pencil_classifier_1d_layouter_layout_for_timing( &((*this_).pencil_classifier_1d_layouter), font_layout );
216 :
217 : /* calculate the feature shapes */
218 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
219 :
220 : /* calculate the relationship shapes for a timing diagram */
221 0 : pencil_relationship_1d_layouter_layout_for_timing( &((*this_).pencil_relationship_1d_layouter) );
222 :
223 : /* layout labels of relationships */
224 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
225 : }
226 : else
227 : {
228 : /* store the classifier bounds into input_data_layouter_t */
229 0 : pencil_classifier_2d_layouter_estimate_bounds( &((*this_).pencil_classifier_2d_layouter), font_layout );
230 :
231 : /* move the classifiers to avoid overlaps */
232 0 : pencil_classifier_2d_layouter_move_to_avoid_overlaps( &((*this_).pencil_classifier_2d_layouter) );
233 :
234 : /* parent classifiers embrace their children step by step */
235 0 : pencil_classifier_2d_layouter_embrace_children( &((*this_).pencil_classifier_2d_layouter), font_layout );
236 :
237 : /* classifiers embrace all children at once and move them if there is space available */
238 0 : pencil_classifier_2d_layouter_move_and_embrace_children( &((*this_).pencil_classifier_2d_layouter), font_layout );
239 :
240 : /* calculate the feature shapes */
241 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
242 :
243 0 : if ( DATA_DIAGRAM_TYPE_BOX_DIAGRAM == diag_type )
244 : {
245 : /* hide relationships in simple list and box diagrams */
246 0 : pencil_relationship_2d_layouter_layout_void( &((*this_).pencil_relationship_2d_layouter) );
247 : }
248 0 : else if (( DATA_DIAGRAM_TYPE_UML_COMMUNICATION_DIAGRAM == diag_type )||( DATA_DIAGRAM_TYPE_INTERACTION_OVERVIEW_DIAGRAM == diag_type ))
249 : {
250 : /* calculate the relationship shapes for a communication diagram or an interaction overview diagram (scenario-relations only) */
251 0 : pencil_relationship_2d_layouter_layout_for_communication( &((*this_).pencil_relationship_2d_layouter) );
252 : }
253 : else
254 : {
255 : /* calculate the relationship shapes */
256 0 : pencil_relationship_2d_layouter_layout_standard( &((*this_).pencil_relationship_2d_layouter) );
257 : }
258 :
259 : /* hide containment relationships if children are embraced */
260 0 : pencil_classifier_2d_layouter_hide_relations_of_embraced_children( &((*this_).pencil_classifier_2d_layouter) );
261 :
262 : /* layout labels of features and relationships */
263 0 : pencil_feat_label_layouter_do_layout( &((*this_).feature_label_layouter), font_layout );
264 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
265 : }
266 :
267 0 : U8_TRACE_END();
268 0 : }
269 :
270 0 : void pencil_layouter_private_propose_default_classifier_size ( pencil_layouter_t *this_ )
271 : {
272 0 : U8_TRACE_BEGIN();
273 :
274 : /* determine grid cell size */
275 0 : const double grid_width = geometry_non_linear_scale_get_grid_distances ( &((*this_).x_scale) );
276 0 : const double grid_height = geometry_non_linear_scale_get_grid_distances ( &((*this_).y_scale) );
277 0 : double cell_width = grid_width;
278 0 : double cell_height = grid_height;
279 :
280 : /* check if the grid has enough points for all classifiers */
281 0 : const uint_fast32_t interv_count_x = geometry_non_linear_scale_get_grid_intervals ( &((*this_).x_scale) );
282 0 : const uint_fast32_t interv_count_y = geometry_non_linear_scale_get_grid_intervals ( &((*this_).y_scale) );
283 0 : const uint_fast32_t inner_point_count = (interv_count_x-1)*(interv_count_y-1);
284 0 : const uint_fast32_t c_count = pencil_layout_data_get_visible_classifier_count ( &((*this_).layout_data) );
285 0 : if ( inner_point_count < c_count )
286 : {
287 : /* many classifiers share the same location */
288 : /* default size is calculated based on count and size, not on grid */
289 : /* get the diagram data */
290 : const layout_diagram_t *const the_diagram
291 0 : = pencil_layout_data_get_diagram_ptr( &((*this_).layout_data) );
292 : const geometry_rectangle_t *const diagram_draw_area
293 0 : = layout_diagram_get_draw_area_const( the_diagram );
294 0 : const double draw_width = geometry_rectangle_get_width ( diagram_draw_area );
295 0 : const double draw_height = geometry_rectangle_get_height ( diagram_draw_area );
296 0 : const uint_fast32_t border = 1;
297 : uint_fast32_t rows;
298 : uint_fast32_t columns;
299 0 : if ( c_count <= 6 )
300 : {
301 0 : columns = 2;
302 0 : rows = (c_count+1)/2;
303 : }
304 0 : else if ( c_count <= 12 )
305 : {
306 0 : columns = 3;
307 0 : rows = (c_count+2)/3;
308 : }
309 0 : else if ( c_count <= 24 )
310 : {
311 0 : columns = 4;
312 0 : rows = (c_count+3)/4;
313 : }
314 : else
315 : {
316 0 : columns = 5;
317 0 : rows = (c_count+4)/5;
318 : }
319 0 : cell_width = draw_width / (columns+border);
320 0 : cell_height = draw_height / (rows+border);
321 : }
322 :
323 : /* determine standard gap between objects */
324 0 : const double gap = pencil_size_get_preferred_object_distance( &((*this_).pencil_size) );
325 :
326 : /* set the default size to grid cell minus a gap on each side, minus extra gap on top for containers */
327 0 : geometry_dimensions_t *const default_size = &((*this_).default_classifier_size);
328 0 : geometry_dimensions_reinit( default_size, cell_width, cell_height );
329 0 : const double x_space = 3.0 * gap; /* space for enclosing parents and for relationships */
330 0 : const double y_space = 4.0 * gap; /* space for enclosing parents (including title-line) and for relationships */
331 0 : geometry_dimensions_expand ( default_size, -x_space, -y_space ); /* ensures non-negative values */
332 :
333 : /* for aesthetic reasons, ensure that the default dimension is more wide than high */
334 0 : const double w = geometry_dimensions_get_width( default_size );
335 0 : const double h = geometry_dimensions_get_height( default_size );
336 0 : if ( w * 0.75 < h )
337 : {
338 0 : geometry_dimensions_reinit( default_size, w, w * 0.75 );
339 : }
340 :
341 0 : U8_TRACE_END();
342 0 : }
343 :
344 0 : pencil_error_t pencil_layouter_get_object_id_at_pos ( const pencil_layouter_t *this_,
345 : double x,
346 : double y,
347 : double snap_distance,
348 : pencil_type_filter_t filter,
349 : data_full_id_t* out_selected_id,
350 : data_full_id_t* out_surrounding_id )
351 : {
352 0 : U8_TRACE_BEGIN();
353 0 : assert( NULL != out_selected_id );
354 0 : assert( NULL != out_surrounding_id );
355 :
356 0 : pencil_error_t result = PENCIL_ERROR_NONE;
357 0 : data_full_id_reinit_void( out_selected_id );
358 0 : data_full_id_reinit_void( out_surrounding_id );
359 : const layout_diagram_t *the_diagram;
360 0 : the_diagram = pencil_layout_data_get_diagram_const( &((*this_).layout_data) );
361 : const data_diagram_t *diagram_data;
362 0 : diagram_data = layout_diagram_get_data_const ( the_diagram );
363 :
364 : /* get bounding box */
365 : const geometry_rectangle_t *diagram_bounds;
366 0 : diagram_bounds = layout_diagram_get_bounds_const( the_diagram );
367 :
368 0 : if ( geometry_rectangle_contains( diagram_bounds, x, y ) )
369 : {
370 : /* check the relationship shapes */
371 : {
372 0 : result = pencil_layouter_private_get_relationship_id_at_pos( this_,
373 : x,
374 : y,
375 : snap_distance,
376 : out_selected_id
377 : );
378 : }
379 :
380 : /* determine a feature at the given position */
381 0 : if ( ! data_full_id_is_valid( out_selected_id ) )
382 : {
383 0 : result = pencil_layouter_private_get_feature_id_at_pos( this_,
384 : x,
385 : y,
386 : filter,
387 : out_selected_id,
388 : out_surrounding_id
389 : );
390 : }
391 :
392 : /* determine a classifier at the given position */
393 0 : if ( ! data_full_id_is_valid( out_selected_id ) )
394 : {
395 0 : result = pencil_layouter_private_get_classifier_id_at_pos( this_,
396 : x,
397 : y,
398 : out_selected_id,
399 : out_surrounding_id
400 : );
401 : }
402 :
403 : /* fallback: return the diagram */
404 0 : if ( ! data_full_id_is_valid( out_selected_id ) )
405 : {
406 0 : data_full_id_reinit_by_table_and_id ( out_selected_id,
407 : DATA_TABLE_DIAGRAM,
408 : data_diagram_get_row_id(diagram_data),
409 : DATA_TABLE_VOID,
410 : DATA_ROW_ID_VOID
411 : );
412 : }
413 0 : if ( ! data_full_id_is_valid( out_surrounding_id ) )
414 : {
415 0 : data_full_id_reinit_by_table_and_id ( out_surrounding_id,
416 : DATA_TABLE_DIAGRAM,
417 : data_diagram_get_row_id(diagram_data),
418 : DATA_TABLE_VOID,
419 : DATA_ROW_ID_VOID
420 : );
421 : }
422 : }
423 : else
424 : {
425 0 : U8_TRACE_INFO( "given location outside diagram or no diagram chosen" );
426 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
427 : }
428 :
429 0 : U8_TRACE_END_ERR( result );
430 0 : return result;
431 : }
432 :
433 0 : pencil_error_t pencil_layouter_private_get_classifier_id_at_pos ( const pencil_layouter_t *this_,
434 : double x,
435 : double y,
436 : data_full_id_t* out_selected_id,
437 : data_full_id_t* out_surrounding_id )
438 : {
439 0 : U8_TRACE_BEGIN();
440 0 : assert( NULL != out_selected_id );
441 0 : assert( NULL != out_surrounding_id );
442 :
443 0 : pencil_error_t result = PENCIL_ERROR_OUT_OF_BOUNDS;
444 :
445 : /* get draw area */
446 : const layout_diagram_t *const the_diagram
447 0 : = pencil_layout_data_get_diagram_const( &((*this_).layout_data) );
448 : const geometry_rectangle_t *const diagram_draw_area
449 0 : = layout_diagram_get_draw_area_const( the_diagram );
450 :
451 0 : if ( geometry_rectangle_contains( diagram_draw_area, x, y ) )
452 : {
453 : /* iterate over all classifiers */
454 : const uint32_t count
455 0 : = pencil_layout_data_get_visible_classifier_count ( &((*this_).layout_data) );
456 0 : double surrounding_classifier_area = geometry_rectangle_get_area( diagram_draw_area );
457 :
458 0 : for ( uint32_t index = 0; index < count; index ++ )
459 : {
460 : const layout_visible_classifier_t *const visible_classifier
461 0 : = pencil_layout_data_get_visible_classifier_const ( &((*this_).layout_data), index );
462 : const geometry_rectangle_t *const classifier_symbol_box
463 0 : = layout_visible_classifier_get_symbol_box_const ( visible_classifier );
464 : const geometry_rectangle_t *const classifier_space
465 0 : = layout_visible_classifier_get_space_const ( visible_classifier );
466 : const geometry_rectangle_t *const classifier_label_box
467 0 : = layout_visible_classifier_get_label_box_const( visible_classifier );
468 :
469 0 : if ( geometry_rectangle_contains( classifier_symbol_box, x, y )
470 0 : || geometry_rectangle_contains( classifier_label_box, x, y ) )
471 : {
472 0 : if ( geometry_rectangle_contains( classifier_space, x, y ) )
473 : {
474 : /* surrounding classifier is found. select it if it is the smallest found area */
475 0 : const double current_classifier_area = geometry_rectangle_get_area( classifier_space );
476 0 : if ( current_classifier_area < surrounding_classifier_area )
477 : {
478 0 : surrounding_classifier_area = current_classifier_area;
479 0 : data_full_id_reinit_by_table_and_id ( out_surrounding_id,
480 : DATA_TABLE_DIAGRAMELEMENT,
481 : layout_visible_classifier_get_diagramelement_id( visible_classifier ),
482 : DATA_TABLE_CLASSIFIER,
483 : layout_visible_classifier_get_classifier_id( visible_classifier )
484 : );
485 : }
486 : }
487 : else
488 : {
489 : /* classifier is found */
490 0 : data_full_id_reinit_by_table_and_id ( out_selected_id,
491 : DATA_TABLE_DIAGRAMELEMENT,
492 : layout_visible_classifier_get_diagramelement_id( visible_classifier ),
493 : DATA_TABLE_CLASSIFIER,
494 : layout_visible_classifier_get_classifier_id( visible_classifier )
495 : );
496 :
497 0 : result = PENCIL_ERROR_NONE;
498 : }
499 : }
500 : }
501 : }
502 :
503 0 : U8_TRACE_END_ERR( result );
504 0 : return result;
505 : }
506 :
507 0 : pencil_error_t pencil_layouter_private_get_feature_id_at_pos ( const pencil_layouter_t *this_,
508 : double x,
509 : double y,
510 : pencil_type_filter_t filter,
511 : data_full_id_t* out_selected_id,
512 : data_full_id_t* out_surrounding_id )
513 : {
514 0 : U8_TRACE_BEGIN();
515 0 : assert( NULL != out_selected_id );
516 0 : assert( NULL != out_surrounding_id );
517 :
518 0 : pencil_error_t result = PENCIL_ERROR_OUT_OF_BOUNDS;
519 :
520 : /* check all contained features */
521 0 : const uint32_t f_count = pencil_layout_data_get_feature_count( &((*this_).layout_data) );
522 0 : for ( uint32_t f_idx = 0; f_idx < f_count; f_idx ++ )
523 : {
524 : const layout_feature_t *const the_feature
525 0 : = pencil_layout_data_get_feature_const ( &((*this_).layout_data), f_idx );
526 : const geometry_rectangle_t *const feature_symbol_box
527 0 : = layout_feature_get_symbol_box_const ( the_feature );
528 : const geometry_rectangle_t *const feature_label_box
529 0 : = layout_feature_get_label_box_const( the_feature );
530 :
531 0 : if ( geometry_rectangle_contains( feature_symbol_box, x, y )
532 0 : || geometry_rectangle_contains( feature_label_box, x, y ) )
533 : {
534 : /* feature is found */
535 : const data_feature_t *const data_feature
536 0 : = layout_feature_get_data_const ( the_feature );
537 : const layout_visible_classifier_t *const layout_classifier
538 0 : = layout_feature_get_classifier_const ( the_feature );
539 0 : if (( PENCIL_TYPE_FILTER_LIFELINE == filter )
540 0 : &&( DATA_FEATURE_TYPE_LIFELINE == data_feature_get_main_type( data_feature ) ))
541 : {
542 0 : data_full_id_reinit_by_table_and_id ( out_selected_id,
543 : DATA_TABLE_DIAGRAMELEMENT,
544 : layout_visible_classifier_get_diagramelement_id( layout_classifier ),
545 : DATA_TABLE_CLASSIFIER,
546 : layout_visible_classifier_get_classifier_id( layout_classifier )
547 : );
548 : }
549 : else
550 : {
551 0 : data_full_id_reinit_by_table_and_id ( out_selected_id,
552 : DATA_TABLE_FEATURE,
553 : layout_feature_get_feature_id( the_feature ),
554 : DATA_TABLE_CLASSIFIER,
555 : data_feature_get_classifier_row_id( data_feature )
556 : );
557 : }
558 :
559 0 : data_full_id_reinit_by_table_and_id ( out_surrounding_id,
560 : DATA_TABLE_DIAGRAMELEMENT,
561 : layout_visible_classifier_get_diagramelement_id( layout_classifier ),
562 : DATA_TABLE_CLASSIFIER,
563 : layout_visible_classifier_get_classifier_id( layout_classifier )
564 : );
565 :
566 0 : result = PENCIL_ERROR_NONE;
567 : }
568 : }
569 :
570 0 : U8_TRACE_END_ERR( result );
571 0 : return result;
572 : }
573 :
574 0 : pencil_error_t pencil_layouter_private_get_relationship_id_at_pos ( const pencil_layouter_t *this_,
575 : double x,
576 : double y,
577 : double snap_distance,
578 : data_full_id_t* out_selected_id )
579 : {
580 0 : U8_TRACE_BEGIN();
581 0 : assert( NULL != out_selected_id );
582 :
583 0 : pencil_error_t result = PENCIL_ERROR_OUT_OF_BOUNDS;
584 :
585 : const uint32_t count_relations
586 0 : = pencil_layout_data_get_relationship_count ( &((*this_).layout_data) );
587 0 : uint32_t matching_relations_found = 0;
588 0 : for ( uint32_t rel_index = 0; rel_index < count_relations; rel_index ++ )
589 : {
590 : const layout_relationship_t *const the_relationship
591 0 : = pencil_layout_data_get_relationship_const( &((*this_).layout_data), rel_index );
592 : const geometry_connector_t *const relationship_shape
593 0 : = layout_relationship_get_shape_const( the_relationship );
594 : const geometry_rectangle_t *const rel_label_box
595 0 : = layout_relationship_get_label_box_const( the_relationship );
596 :
597 0 : if ( geometry_connector_is_close( relationship_shape, x, y, snap_distance )
598 0 : || geometry_rectangle_contains( rel_label_box, x, y ) )
599 : {
600 : /* ensure that every relation at that location can be selected by small mouse movements */
601 0 : if ( ((uint32_t)(x+y))%(matching_relations_found+1) == 0 )
602 : {
603 : const layout_relationship_t *current_relation;
604 0 : current_relation = pencil_layout_data_get_relationship_const ( &((*this_).layout_data), rel_index );
605 : const data_relationship_t *relation_data;
606 0 : relation_data = layout_relationship_get_data_const( current_relation );
607 :
608 0 : data_full_id_reinit_by_table_and_id ( out_selected_id,
609 : DATA_TABLE_RELATIONSHIP,
610 : data_relationship_get_row_id( relation_data ),
611 : DATA_TABLE_VOID,
612 : DATA_ROW_ID_VOID
613 : );
614 0 : result = PENCIL_ERROR_NONE;
615 : }
616 0 : matching_relations_found ++;
617 : }
618 : }
619 :
620 0 : U8_TRACE_END_ERR( result );
621 0 : return result;
622 : }
623 :
624 0 : pencil_error_t pencil_layouter_get_classifier_order_at_pos ( const pencil_layouter_t *this_,
625 : data_classifier_type_t c_type,
626 : double x,
627 : double y,
628 : double snap_distance,
629 : layout_order_t* out_layout_order )
630 : {
631 0 : U8_TRACE_BEGIN();
632 0 : assert ( NULL != out_layout_order );
633 :
634 0 : pencil_error_t result = PENCIL_ERROR_NONE;
635 :
636 : /* get the bounding box of the diagram */
637 : const layout_diagram_t *const the_diagram
638 0 : = pencil_layout_data_get_diagram_const( &((*this_).layout_data) );
639 : const geometry_rectangle_t *const diagram_bounds
640 0 : = layout_diagram_get_bounds_const( the_diagram );
641 : const geometry_rectangle_t *const diagram_draw_area
642 0 : = layout_diagram_get_draw_area_const( the_diagram );
643 :
644 : /* get the diagram type */
645 : const data_diagram_t *const diagram_data
646 0 : = layout_diagram_get_data_const ( the_diagram );
647 : const data_diagram_type_t diag_type
648 0 : = data_diagram_get_diagram_type ( diagram_data );
649 :
650 : /* get the classifier type */
651 : const bool scenario_semantics
652 0 : = data_rules_classifier_has_scenario_semantics( &((*this_).rules),
653 : diag_type,
654 : c_type
655 : );
656 :
657 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
658 : {
659 0 : layout_order_init_empty( out_layout_order );
660 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
661 : }
662 : else
663 : {
664 0 : if ((( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )&& scenario_semantics)
665 0 : || (( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type)&&( ! scenario_semantics)))
666 0 : {
667 : /* classifiers are a horizontal list */
668 0 : const double draw_left = geometry_rectangle_get_left(diagram_draw_area);
669 0 : const double draw_right = geometry_rectangle_get_right(diagram_draw_area);
670 : int32_t list_order;
671 0 : if ( x <= draw_left )
672 : {
673 0 : list_order = INT32_MIN;
674 : }
675 0 : else if ( x >= draw_right )
676 : {
677 0 : list_order = INT32_MAX;
678 : }
679 : else
680 : {
681 0 : list_order = ((uint32_t)(( x - draw_left ) / ( draw_right - draw_left ) * UINT32_MAX));
682 0 : list_order += INT32_MIN;
683 : }
684 0 : layout_order_init_list( out_layout_order, list_order );
685 : }
686 0 : else if ((( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )&& scenario_semantics)
687 0 : || ( DATA_DIAGRAM_TYPE_LIST == diag_type )
688 0 : || (( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type)&&( ! scenario_semantics)))
689 0 : {
690 : /* classifiers are a vertical list */
691 0 : const double draw_top = geometry_rectangle_get_top(diagram_draw_area);
692 0 : const double draw_bottom = geometry_rectangle_get_bottom(diagram_draw_area);
693 : int32_t list_order;
694 0 : if ( y <= draw_top )
695 : {
696 0 : list_order = INT32_MIN;
697 : }
698 0 : else if ( y >= draw_bottom )
699 : {
700 0 : list_order = INT32_MAX;
701 : }
702 : else
703 : {
704 0 : list_order = ((uint32_t)(( y - draw_top ) / ( draw_bottom - draw_top ) * UINT32_MAX));
705 0 : list_order += INT32_MIN;
706 : }
707 0 : layout_order_init_list( out_layout_order, list_order );
708 : }
709 : else
710 : {
711 : /* classifiers are x/y arranged */
712 : const int32_t x_order
713 0 : = geometry_non_linear_scale_get_order( &((*this_).x_scale), x, snap_distance );
714 : const int32_t y_order
715 0 : = geometry_non_linear_scale_get_order( &((*this_).y_scale), y, snap_distance );
716 0 : layout_order_init_x_y( out_layout_order, x_order, y_order );
717 : }
718 : }
719 :
720 0 : U8_TRACE_END_ERR( result );
721 0 : return result;
722 : }
723 :
724 0 : pencil_error_t pencil_layouter_get_feature_order_at_pos ( const pencil_layouter_t *this_,
725 : const data_feature_t *feature_ptr,
726 : double x,
727 : double y,
728 : layout_order_t* out_layout_order )
729 : {
730 0 : U8_TRACE_BEGIN();
731 0 : assert ( NULL != out_layout_order );
732 0 : assert ( NULL != feature_ptr );
733 :
734 0 : pencil_error_t result = PENCIL_ERROR_NONE;
735 :
736 : /* get data of feature */
737 : data_row_id_t parent_classifier_id;
738 : data_feature_type_t feature_type;
739 0 : feature_type = data_feature_get_main_type ( feature_ptr );
740 0 : parent_classifier_id = data_feature_get_classifier_row_id ( feature_ptr );
741 :
742 : /* get the bounding box of the diagram */
743 : const layout_diagram_t *const the_diagram
744 0 : = pencil_layout_data_get_diagram_const( &((*this_).layout_data) );
745 : const geometry_rectangle_t *const diagram_bounds
746 0 : = layout_diagram_get_bounds_const( the_diagram );
747 :
748 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
749 : {
750 0 : layout_order_init_empty( out_layout_order );
751 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
752 : }
753 0 : else if ( DATA_ROW_ID_VOID == parent_classifier_id ) {
754 0 : U8_LOG_WARNING( "feature to move has no parent classifier!" );
755 0 : layout_order_init_empty( out_layout_order );
756 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
757 : }
758 : else
759 : {
760 : /* iterate over all classifiers, search the closest_parent_instance */
761 0 : const layout_visible_classifier_t *closest_parent_instance = NULL;
762 : const uint32_t classfy_count
763 0 : = pencil_layout_data_get_visible_classifier_count ( &((*this_).layout_data) );
764 0 : for ( uint32_t classfy_index = 0; classfy_index < classfy_count; classfy_index ++ )
765 : {
766 : const layout_visible_classifier_t *const visible_classifier
767 0 : = pencil_layout_data_get_visible_classifier_const ( &((*this_).layout_data), classfy_index );
768 : const data_row_id_t classfy_id
769 0 : = layout_visible_classifier_get_classifier_id ( visible_classifier );
770 0 : if ( parent_classifier_id == classfy_id )
771 : {
772 0 : if ( NULL == closest_parent_instance )
773 : {
774 0 : closest_parent_instance = visible_classifier;
775 : }
776 : else
777 : {
778 : const geometry_rectangle_t *const classfier_symbol_box
779 0 : = layout_visible_classifier_get_symbol_box_const ( visible_classifier );
780 : const geometry_rectangle_t *const closest_parent_symbol_box
781 0 : = layout_visible_classifier_get_symbol_box_const ( closest_parent_instance );
782 0 : const double classfy_distance = geometry_rectangle_calc_chess_distance( classfier_symbol_box, x, y );
783 : const double closest_parent_distance
784 0 : = geometry_rectangle_calc_chess_distance( closest_parent_symbol_box, x, y );
785 0 : if ( classfy_distance < closest_parent_distance )
786 : {
787 0 : closest_parent_instance = visible_classifier;
788 : }
789 : }
790 : }
791 : }
792 :
793 0 : if ( NULL != closest_parent_instance )
794 : {
795 0 : switch (feature_type)
796 : {
797 0 : case DATA_FEATURE_TYPE_PROPERTY: /* or */
798 : case DATA_FEATURE_TYPE_OPERATION: /* or */
799 : case DATA_FEATURE_TYPE_TAGGED_VALUE: /* or */
800 : default: /* this may happen if a new database file has been read by an old program version */
801 : {
802 0 : int32_t max_order_above = INT32_MIN;
803 0 : int32_t min_order_below = INT32_MAX;
804 : /* iterate over all contained features */
805 0 : const uint32_t f_count = pencil_layout_data_get_feature_count( &((*this_).layout_data) );
806 0 : for ( uint32_t f_idx = 0; f_idx < f_count; f_idx ++ )
807 : {
808 : /* check if feature belongs to same parent classifier */
809 : const layout_feature_t *const the_feature
810 0 : = pencil_layout_data_get_feature_const ( &((*this_).layout_data), f_idx );
811 : const layout_visible_classifier_t *const vis_classfy
812 0 : = layout_feature_get_classifier_const ( the_feature );
813 0 : if ( closest_parent_instance == vis_classfy )
814 : {
815 : /* check if feature is not the moved one */
816 : const data_feature_t *const data_feature
817 0 : = layout_feature_get_data_const ( the_feature );
818 0 : if ( data_feature_get_row_id ( feature_ptr ) != data_feature_get_row_id ( data_feature ) )
819 : {
820 : const int32_t list_order
821 0 : = data_feature_get_list_order( data_feature );
822 : const geometry_rectangle_t *const feature_symbol_box
823 0 : = layout_feature_get_symbol_box_const ( the_feature );
824 0 : if ( y < geometry_rectangle_get_center_y( feature_symbol_box ) )
825 : {
826 0 : if ( list_order < min_order_below ) { min_order_below = list_order; }
827 : }
828 : else
829 : {
830 0 : if ( list_order > max_order_above ) { max_order_above = list_order; }
831 : }
832 : }
833 : }
834 : }
835 :
836 0 : if ( max_order_above == INT32_MIN )
837 : {
838 0 : if ( min_order_below == INT32_MAX )
839 : {
840 : /* nothing above, nothing below */
841 0 : layout_order_init_list( out_layout_order, 0 );
842 : }
843 : else
844 : {
845 : /* nothing above */
846 0 : layout_order_init_list( out_layout_order, min_order_below - 32768 );
847 : }
848 : }
849 : else
850 : {
851 0 : if ( min_order_below == INT32_MAX )
852 : {
853 : /* nothing below */
854 0 : layout_order_init_list( out_layout_order, max_order_above + 32768 );
855 : }
856 : else
857 : {
858 : /* regular interval */
859 0 : layout_order_init_list( out_layout_order, (max_order_above + min_order_below)/2 );
860 : }
861 : }
862 : }
863 0 : break;
864 :
865 0 : case DATA_FEATURE_TYPE_PORT: /* or */
866 : case DATA_FEATURE_TYPE_PROVIDED_INTERFACE: /* or */
867 : case DATA_FEATURE_TYPE_REQUIRED_INTERFACE: /* or */
868 : case DATA_FEATURE_TYPE_IN_PORT_PIN: /* or */
869 : case DATA_FEATURE_TYPE_OUT_PORT_PIN: /* or */
870 : case DATA_FEATURE_TYPE_ENTRY: /* or */
871 : case DATA_FEATURE_TYPE_EXIT:
872 : {
873 : const geometry_rectangle_t *const closest_parent_symbol_box
874 0 : = layout_visible_classifier_get_symbol_box_const ( closest_parent_instance );
875 0 : const double center_x = geometry_rectangle_get_center_x( closest_parent_symbol_box );
876 0 : const double center_y = geometry_rectangle_get_center_y( closest_parent_symbol_box );
877 0 : const double width = geometry_rectangle_get_width( closest_parent_symbol_box );
878 0 : const double height = geometry_rectangle_get_height( closest_parent_symbol_box );
879 0 : const double delta_x = x - center_x;
880 0 : const double delty_y = y - center_y;
881 0 : const double relative_delta_x = delta_x * height;
882 0 : const double relative_delta_y = delty_y * width;
883 0 : const double distance_x = ( x < center_x ) ? (center_x - x) : (x - center_x);
884 0 : const double distance_y = ( y < center_y ) ? (center_y - y) : (y - center_y);
885 0 : const double relative_dist_x = distance_x * height;
886 0 : const double relative_dist_y = distance_y * width;
887 : int32_t order;
888 0 : if ( relative_dist_x > relative_dist_y )
889 : {
890 0 : if ( x < center_x )
891 : {
892 : /* x,y is on left side, order is between 0 and INT32_MAX/2 */
893 0 : order = INT32_MAX*(relative_delta_y/(relative_dist_x+0.1)*0.25+0.25);
894 : }
895 : else
896 : {
897 : /* x,y is on right side, order is between INT32_MIN and INT32_MIN/2 */
898 0 : order = INT32_MIN*(relative_delta_y/(relative_dist_x+0.1)*0.25+0.75);
899 : }
900 : }
901 : else
902 : {
903 0 : if ( y < center_y )
904 : {
905 : /* x,y is on upper side, order is between INT32_MIN/2 and 0 */
906 0 : order = INT32_MIN*(relative_delta_x/(relative_dist_y+0.1)*0.25+0.25);
907 : }
908 : else
909 : {
910 : /* x,y is on lower side, order is between INT32_MAX/2 and INT32_MAX */
911 0 : order = INT32_MAX*(relative_delta_x/(relative_dist_y+0.1)*0.25+0.75);
912 : }
913 : }
914 0 : layout_order_init_list( out_layout_order, order );
915 : }
916 0 : break;
917 :
918 0 : case DATA_FEATURE_TYPE_LIFELINE:
919 : {
920 0 : U8_TRACE_INFO( "feature to move is a lifeline and therefore cannot move." );
921 0 : layout_order_init_empty( out_layout_order );
922 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
923 : }
924 0 : break;
925 : }
926 : }
927 : else
928 : {
929 0 : U8_LOG_WARNING( "parent classifier of feature is not visible; possibly array size too small?" );
930 0 : layout_order_init_empty( out_layout_order );
931 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
932 : }
933 : }
934 :
935 0 : U8_TRACE_END_ERR( result );
936 0 : return result;
937 : }
938 :
939 0 : pencil_error_t pencil_layouter_get_relationship_order_at_pos ( const pencil_layouter_t *this_,
940 : double x,
941 : double y,
942 : layout_order_t* out_layout_order )
943 : {
944 0 : U8_TRACE_BEGIN();
945 0 : assert ( NULL != out_layout_order );
946 :
947 0 : pencil_error_t result = PENCIL_ERROR_NONE;
948 :
949 : /* get the bounding box of the diagram */
950 : const layout_diagram_t *const the_diagram
951 0 : = pencil_layout_data_get_diagram_const( &((*this_).layout_data) );
952 : const geometry_rectangle_t *const diagram_bounds
953 0 : = layout_diagram_get_bounds_const( the_diagram );
954 : const geometry_rectangle_t *const diagram_draw_area
955 0 : = layout_diagram_get_draw_area_const( the_diagram );
956 :
957 : /* get the diagram type */
958 : const data_diagram_t *const diagram_data
959 0 : = layout_diagram_get_data_const ( the_diagram );
960 : const data_diagram_type_t diag_type
961 0 : = data_diagram_get_diagram_type ( diagram_data );
962 :
963 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
964 : {
965 0 : layout_order_init_empty( out_layout_order );
966 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
967 : }
968 : else
969 : {
970 0 : if (( DATA_DIAGRAM_TYPE_BOX_DIAGRAM == diag_type )
971 0 : || ( DATA_DIAGRAM_TYPE_LIST == diag_type ))
972 : {
973 : /* relationships are hidden in lists and box-diagrams */
974 0 : layout_order_init_empty( out_layout_order );
975 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
976 : }
977 0 : else if (( DATA_DIAGRAM_TYPE_UML_COMMUNICATION_DIAGRAM == diag_type )||( DATA_DIAGRAM_TYPE_INTERACTION_OVERVIEW_DIAGRAM == diag_type ))
978 : {
979 : /* communication diagrams and interaction overview diagrams do not care about list_orders of relationships */
980 0 : layout_order_init_empty( out_layout_order );
981 : }
982 0 : else if ( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )
983 : {
984 0 : const double draw_top = geometry_rectangle_get_top(diagram_draw_area);
985 0 : const double draw_bottom = geometry_rectangle_get_bottom(diagram_draw_area);
986 : int32_t list_order;
987 0 : if ( y <= draw_top )
988 : {
989 0 : list_order = INT32_MIN;
990 : }
991 0 : else if ( y >= draw_bottom )
992 : {
993 0 : list_order = INT32_MAX;
994 : }
995 : else
996 : {
997 0 : list_order = ((uint32_t)(( y - draw_top ) / ( draw_bottom - draw_top ) * UINT32_MAX));
998 0 : list_order += INT32_MIN;
999 : }
1000 0 : layout_order_init_list( out_layout_order, list_order );
1001 : }
1002 0 : else if ( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )
1003 : {
1004 0 : const double draw_left = geometry_rectangle_get_left(diagram_draw_area);
1005 0 : const double draw_right = geometry_rectangle_get_right(diagram_draw_area);
1006 : int32_t list_order;
1007 0 : if ( x <= draw_left )
1008 : {
1009 0 : list_order = INT32_MIN;
1010 : }
1011 0 : else if ( x >= draw_right )
1012 : {
1013 0 : list_order = INT32_MAX;
1014 : }
1015 : else
1016 : {
1017 0 : list_order = ((uint32_t)(( x - draw_left ) / ( draw_right - draw_left ) * UINT32_MAX));
1018 0 : list_order += INT32_MIN;
1019 : }
1020 0 : layout_order_init_list( out_layout_order, list_order );
1021 : }
1022 : else
1023 : {
1024 : /* all other diagram types do not care about list_orders of relationships */
1025 0 : layout_order_init_empty( out_layout_order );
1026 : }
1027 : }
1028 :
1029 0 : U8_TRACE_END_ERR( result );
1030 0 : return result;
1031 : }
1032 :
1033 :
1034 : /*
1035 : Copyright 2017-2024 Andreas Warnke
1036 :
1037 : Licensed under the Apache License, Version 2.0 (the "License");
1038 : you may not use this file except in compliance with the License.
1039 : You may obtain a copy of the License at
1040 :
1041 : http://www.apache.org/licenses/LICENSE-2.0
1042 :
1043 : Unless required by applicable law or agreed to in writing, software
1044 : distributed under the License is distributed on an "AS IS" BASIS,
1045 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1046 : See the License for the specific language governing permissions and
1047 : limitations under the License.
1048 : */
|