Line data Source code
1 : /* File: pencil_layouter.c; Copyright and License: see below */
2 :
3 : #include "pencil_layouter.h"
4 : #include "geometry/geometry_non_linear_scale.h"
5 : #include "u8/u8_trace.h"
6 : #include <pango/pangocairo.h>
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #include <math.h>
10 :
11 0 : void pencil_layouter_init( pencil_layouter_t *this_,
12 : const data_visible_set_t *input_data,
13 : const data_profile_part_t *profile )
14 : {
15 0 : U8_TRACE_BEGIN();
16 0 : assert( NULL != input_data );
17 0 : assert( NULL != profile );
18 :
19 0 : pencil_size_init_empty( &((*this_).pencil_size) );
20 0 : geometry_grid_init( &((*this_).grid), GEOMETRY_GRID_KIND_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 : layout_visible_set_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 0 : &((*this_).grid),
46 : &((*this_).feature_layouter)
47 : );
48 0 : pencil_classifier_1d_layouter_init( &((*this_).pencil_classifier_1d_layouter),
49 : &((*this_).layout_data),
50 : profile,
51 0 : &((*this_).pencil_size)
52 : );
53 0 : pencil_relationship_2d_layouter_init( &((*this_).pencil_relationship_2d_layouter),
54 : &((*this_).layout_data),
55 : profile,
56 0 : &((*this_).pencil_size)
57 : );
58 0 : pencil_relationship_1d_layouter_init( &((*this_).pencil_relationship_1d_layouter),
59 : &((*this_).layout_data),
60 : profile,
61 0 : &((*this_).pencil_size)
62 : );
63 0 : pencil_rel_label_layouter_init( &((*this_).relationship_label_layouter),
64 : &((*this_).layout_data),
65 : profile,
66 0 : &((*this_).pencil_size)
67 : );
68 0 : U8_TRACE_END();
69 0 : }
70 :
71 0 : void pencil_layouter_reinit( pencil_layouter_t *this_,
72 : const data_visible_set_t *input_data,
73 : const data_profile_part_t *profile )
74 : {
75 0 : U8_TRACE_BEGIN();
76 0 : assert( NULL != input_data );
77 0 : assert( NULL != profile );
78 0 : pencil_layouter_destroy( this_ );
79 0 : pencil_layouter_init( this_, input_data, profile );
80 0 : U8_TRACE_END();
81 0 : }
82 :
83 0 : void pencil_layouter_destroy( pencil_layouter_t *this_ )
84 : {
85 0 : U8_TRACE_BEGIN();
86 :
87 0 : pencil_rel_label_layouter_destroy( &((*this_).relationship_label_layouter) );
88 0 : pencil_relationship_1d_layouter_destroy( &((*this_).pencil_relationship_1d_layouter) );
89 0 : pencil_relationship_2d_layouter_destroy( &((*this_).pencil_relationship_2d_layouter) );
90 0 : pencil_classifier_1d_layouter_destroy( &((*this_).pencil_classifier_1d_layouter) );
91 0 : pencil_classifier_2d_layouter_destroy( &((*this_).pencil_classifier_2d_layouter) );
92 0 : pencil_feat_label_layouter_destroy( &((*this_).feature_label_layouter) );
93 0 : pencil_feature_layouter_destroy( &((*this_).feature_layouter) );
94 :
95 0 : pencil_diagram_painter_destroy( &((*this_).diagram_painter) );
96 :
97 0 : pencil_size_destroy( &((*this_).pencil_size) );
98 0 : geometry_grid_destroy( &((*this_).grid) );
99 0 : geometry_dimensions_destroy( &((*this_).default_classifier_size) );
100 0 : data_rules_destroy( &((*this_).rules) );
101 :
102 0 : layout_visible_set_destroy( &((*this_).layout_data) );
103 :
104 0 : U8_TRACE_END();
105 0 : }
106 :
107 0 : void pencil_layouter_define_grid ( pencil_layouter_t *this_,
108 : geometry_rectangle_t diagram_bounds,
109 : PangoLayout *font_layout )
110 : {
111 0 : U8_TRACE_BEGIN();
112 0 : geometry_rectangle_trace( &diagram_bounds );
113 :
114 : /* get the diagram data */
115 : layout_diagram_t *the_diagram;
116 0 : the_diagram = layout_visible_set_get_diagram_ptr( &((*this_).layout_data) );
117 : const data_diagram_t *const diagram_data
118 0 : = layout_diagram_get_data_const ( the_diagram );
119 :
120 : /* calculate the pencil-sizes and the drawing rectangle */
121 0 : const double width = geometry_rectangle_get_width( &diagram_bounds );
122 0 : const double height = geometry_rectangle_get_height( &diagram_bounds );
123 0 : pencil_size_reinit( &((*this_).pencil_size), width, height );
124 :
125 0 : pencil_diagram_painter_do_layout( &((*this_).diagram_painter),
126 : diagram_data,
127 : &diagram_bounds,
128 : (*this_).profile,
129 0 : &((*this_).pencil_size),
130 : font_layout,
131 : the_diagram
132 : );
133 : const geometry_rectangle_t *const diagram_draw_area
134 0 : = layout_diagram_get_draw_area_const( the_diagram );
135 :
136 : /* calculate the axis scales */
137 0 : geometry_rectangle_trace( diagram_draw_area );
138 0 : const double draw_left = geometry_rectangle_get_left( diagram_draw_area );
139 0 : const double draw_top = geometry_rectangle_get_top( diagram_draw_area );
140 0 : const double draw_right = geometry_rectangle_get_right( diagram_draw_area );
141 0 : const double draw_bottom = geometry_rectangle_get_bottom( diagram_draw_area );
142 0 : geometry_grid_reinit( &((*this_).grid), GEOMETRY_GRID_KIND_XY );
143 0 : geometry_non_linear_scale_t *const x_scale = geometry_grid_get_x_scale_ptr( &((*this_).grid) );
144 0 : geometry_non_linear_scale_t *const y_scale = geometry_grid_get_y_scale_ptr( &((*this_).grid) );
145 0 : geometry_non_linear_scale_reinit( x_scale, draw_left, draw_right );
146 0 : geometry_non_linear_scale_reinit( y_scale, draw_top, draw_bottom );
147 :
148 : /* iterate over all classifiers */
149 : const uint32_t count
150 0 : = layout_visible_set_get_visible_classifier_count ( &((*this_).layout_data) );
151 0 : for ( uint32_t index = 0; index < count; index ++ )
152 : {
153 : const layout_visible_classifier_t *const visible_classifier
154 0 : = layout_visible_set_get_visible_classifier_ptr ( &((*this_).layout_data), index );
155 : const data_classifier_t *const classifier_data
156 0 : = layout_visible_classifier_get_classifier_const( visible_classifier );
157 : const uint32_t visible_descendants
158 0 : = layout_visible_set_count_descendants( &((*this_).layout_data), visible_classifier );
159 :
160 : /* adjust the non-linear scales for this classifier (if no contained descendants) */
161 0 : if ( 0 == visible_descendants )
162 : {
163 0 : geometry_non_linear_scale_add_order( x_scale, data_classifier_get_x_order( classifier_data ) );
164 0 : geometry_non_linear_scale_add_order( y_scale, data_classifier_get_y_order( classifier_data ) );
165 : }
166 : }
167 :
168 0 : U8_TRACE_END();
169 0 : }
170 :
171 0 : void pencil_layouter_layout_elements ( pencil_layouter_t *this_, PangoLayout *font_layout )
172 : {
173 0 : U8_TRACE_BEGIN();
174 0 : assert( font_layout != NULL );
175 :
176 : /* get the diagram data */
177 0 : const layout_diagram_t *const the_diagram = layout_visible_set_get_diagram_ptr( &((*this_).layout_data) );
178 0 : const data_diagram_t *const diagram_data = layout_diagram_get_data_const ( the_diagram );
179 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type ( diagram_data );
180 :
181 : /* adjust the default classifier rectangle */
182 0 : pencil_layouter_private_propose_default_classifier_size( this_ );
183 :
184 : /* store the classifier bounds into input_data_layouter_t */
185 0 : if ( DATA_DIAGRAM_TYPE_LIST == diag_type )
186 : {
187 : /* calculate the classifier shapes */
188 0 : pencil_classifier_1d_layouter_layout_for_list( &((*this_).pencil_classifier_1d_layouter), font_layout );
189 :
190 : /* calculate the feature shapes */
191 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
192 :
193 : /* hide relationships in simple list and box diagrams */
194 0 : pencil_relationship_2d_layouter_layout_void( &((*this_).pencil_relationship_2d_layouter) );
195 :
196 : /* layout labels of features */
197 0 : pencil_feat_label_layouter_do_layout( &((*this_).feature_label_layouter), font_layout );
198 : }
199 0 : else if ( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )
200 : {
201 : /* calculate the classifier shapes */
202 0 : pencil_classifier_1d_layouter_layout_for_sequence( &((*this_).pencil_classifier_1d_layouter), font_layout );
203 :
204 : /* calculate the feature shapes */
205 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
206 :
207 : /* calculate the relationship shapes for a sequence diagram */
208 0 : pencil_relationship_1d_layouter_layout_for_sequence( &((*this_).pencil_relationship_1d_layouter) );
209 :
210 : /* layout labels of relationships */
211 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
212 : }
213 0 : else if ( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )
214 : {
215 : /* calculate the classifier shapes */
216 0 : pencil_classifier_1d_layouter_layout_for_timing( &((*this_).pencil_classifier_1d_layouter), font_layout );
217 :
218 : /* calculate the feature shapes */
219 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
220 :
221 : /* calculate the relationship shapes for a timing diagram */
222 0 : pencil_relationship_1d_layouter_layout_for_timing( &((*this_).pencil_relationship_1d_layouter) );
223 :
224 : /* layout labels of relationships */
225 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
226 : }
227 : else
228 : {
229 : /* store the classifier bounds into input_data_layouter_t */
230 0 : pencil_classifier_2d_layouter_estimate_bounds( &((*this_).pencil_classifier_2d_layouter), font_layout );
231 :
232 : /* move the classifiers to avoid overlaps */
233 0 : pencil_classifier_2d_layouter_move_to_avoid_overlaps( &((*this_).pencil_classifier_2d_layouter) );
234 :
235 : /* parent classifiers embrace their children step by step */
236 0 : pencil_classifier_2d_layouter_embrace_children( &((*this_).pencil_classifier_2d_layouter), font_layout );
237 :
238 : /* classifiers embrace all children at once and move them if there is space available */
239 0 : pencil_classifier_2d_layouter_move_and_embrace_children( &((*this_).pencil_classifier_2d_layouter), font_layout );
240 :
241 : /* calculate the feature shapes */
242 0 : pencil_feature_layouter_do_layout( &((*this_).feature_layouter), font_layout );
243 :
244 0 : if ( DATA_DIAGRAM_TYPE_BOX_DIAGRAM == diag_type )
245 : {
246 : /* hide relationships in simple list and box diagrams */
247 0 : pencil_relationship_2d_layouter_layout_void( &((*this_).pencil_relationship_2d_layouter) );
248 : }
249 0 : else if (( DATA_DIAGRAM_TYPE_UML_COMMUNICATION_DIAGRAM == diag_type )||( DATA_DIAGRAM_TYPE_INTERACTION_OVERVIEW_DIAGRAM == diag_type ))
250 : {
251 : /* calculate the relationship shapes for a communication diagram or an interaction overview diagram (scenario-relations only) */
252 0 : pencil_relationship_2d_layouter_layout_for_communication( &((*this_).pencil_relationship_2d_layouter) );
253 : }
254 : else
255 : {
256 : /* calculate the relationship shapes */
257 0 : pencil_relationship_2d_layouter_layout_standard( &((*this_).pencil_relationship_2d_layouter) );
258 : }
259 :
260 : /* hide containment relationships if children are embraced */
261 0 : pencil_classifier_2d_layouter_hide_relations_of_embraced_children( &((*this_).pencil_classifier_2d_layouter) );
262 :
263 : /* layout labels of features and relationships */
264 0 : pencil_feat_label_layouter_do_layout( &((*this_).feature_label_layouter), font_layout );
265 0 : pencil_rel_label_layouter_do_layout( &((*this_).relationship_label_layouter), font_layout );
266 : }
267 :
268 0 : U8_TRACE_END();
269 0 : }
270 :
271 0 : void pencil_layouter_private_propose_default_classifier_size ( pencil_layouter_t *this_ )
272 : {
273 0 : U8_TRACE_BEGIN();
274 :
275 : /* determine grid cell size */
276 0 : const geometry_non_linear_scale_t *const x_scale = geometry_grid_get_x_scale_const( &((*this_).grid) );
277 0 : const geometry_non_linear_scale_t *const y_scale = geometry_grid_get_y_scale_const( &((*this_).grid) );
278 0 : const double grid_width = geometry_non_linear_scale_get_grid_distances( x_scale );
279 0 : const double grid_height = geometry_non_linear_scale_get_grid_distances( y_scale );
280 0 : double cell_width = grid_width;
281 0 : double cell_height = grid_height;
282 :
283 : /* check if the grid has enough points for all classifiers */
284 0 : const uint_fast32_t interv_count_x = geometry_non_linear_scale_get_grid_intervals( x_scale );
285 0 : const uint_fast32_t interv_count_y = geometry_non_linear_scale_get_grid_intervals( y_scale );
286 0 : const uint_fast32_t inner_point_count = (interv_count_x-1)*(interv_count_y-1);
287 0 : const uint_fast32_t c_count = layout_visible_set_get_visible_classifier_count ( &((*this_).layout_data) );
288 0 : if ( inner_point_count < c_count )
289 : {
290 : /* many classifiers share the same location */
291 : /* default size is calculated based on count and size, not on grid */
292 : /* get the diagram data */
293 : const layout_diagram_t *const the_diagram
294 0 : = layout_visible_set_get_diagram_ptr( &((*this_).layout_data) );
295 : const geometry_rectangle_t *const diagram_draw_area
296 0 : = layout_diagram_get_draw_area_const( the_diagram );
297 0 : const double draw_width = geometry_rectangle_get_width ( diagram_draw_area );
298 0 : const double draw_height = geometry_rectangle_get_height ( diagram_draw_area );
299 0 : const uint_fast32_t border = 1;
300 : uint_fast32_t rows;
301 : uint_fast32_t columns;
302 0 : if ( c_count <= 6 )
303 : {
304 0 : columns = 2;
305 0 : rows = (c_count+1)/2;
306 : }
307 0 : else if ( c_count <= 12 )
308 : {
309 0 : columns = 3;
310 0 : rows = (c_count+2)/3;
311 : }
312 0 : else if ( c_count <= 24 )
313 : {
314 0 : columns = 4;
315 0 : rows = (c_count+3)/4;
316 : }
317 : else
318 : {
319 0 : columns = 5;
320 0 : rows = (c_count+4)/5;
321 : }
322 0 : cell_width = draw_width / (columns+border);
323 0 : cell_height = draw_height / (rows+border);
324 : }
325 :
326 : /* determine standard gap between objects */
327 0 : const double gap = pencil_size_get_preferred_object_distance( &((*this_).pencil_size) );
328 :
329 : /* set the default size to grid cell minus a gap on each side, minus extra gap on top for containers */
330 0 : geometry_dimensions_t *const default_size = &((*this_).default_classifier_size);
331 0 : geometry_dimensions_reinit( default_size, cell_width, cell_height );
332 0 : const double x_space = 3.0 * gap; /* space for enclosing parents and for relationships */
333 0 : const double y_space = 4.0 * gap; /* space for enclosing parents (including title-line) and for relationships */
334 0 : geometry_dimensions_expand ( default_size, -x_space, -y_space ); /* ensures non-negative values */
335 :
336 : /* for aesthetic reasons, ensure that the default dimension is more wide than high */
337 0 : const double w = geometry_dimensions_get_width( default_size );
338 0 : const double h = geometry_dimensions_get_height( default_size );
339 0 : if ( w * 0.75 < h )
340 : {
341 0 : geometry_dimensions_reinit( default_size, w, w * 0.75 );
342 : }
343 :
344 0 : U8_TRACE_END();
345 0 : }
346 :
347 0 : pencil_error_t pencil_layouter_get_classifier_order_at_pos ( const pencil_layouter_t *this_,
348 : data_classifier_type_t c_type,
349 : double x,
350 : double y,
351 : double snap_distance,
352 : layout_order_t* out_layout_order )
353 : {
354 0 : U8_TRACE_BEGIN();
355 0 : assert ( NULL != out_layout_order );
356 :
357 0 : pencil_error_t result = PENCIL_ERROR_NONE;
358 :
359 : /* get the bounding box of the diagram */
360 : const layout_diagram_t *const the_diagram
361 0 : = layout_visible_set_get_diagram_const( &((*this_).layout_data) );
362 : const geometry_rectangle_t *const diagram_bounds
363 0 : = layout_diagram_get_bounds_const( the_diagram );
364 : const geometry_rectangle_t *const diagram_draw_area
365 0 : = layout_diagram_get_draw_area_const( the_diagram );
366 :
367 : /* get the diagram type */
368 : const data_diagram_t *const diagram_data
369 0 : = layout_diagram_get_data_const ( the_diagram );
370 : const data_diagram_type_t diag_type
371 0 : = data_diagram_get_diagram_type ( diagram_data );
372 :
373 : /* get the classifier type */
374 : const bool scenario_semantics
375 0 : = data_rules_classifier_has_scenario_semantics( &((*this_).rules),
376 : diag_type,
377 : c_type
378 : );
379 :
380 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
381 : {
382 0 : layout_order_init_empty( out_layout_order );
383 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
384 : }
385 : else
386 : {
387 0 : if ((( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )&& scenario_semantics)
388 0 : || (( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type)&&( ! scenario_semantics)))
389 0 : {
390 : /* classifiers are a horizontal list */
391 0 : const double draw_left = geometry_rectangle_get_left(diagram_draw_area);
392 0 : const double draw_right = geometry_rectangle_get_right(diagram_draw_area);
393 : int32_t list_order;
394 0 : if ( x <= draw_left )
395 : {
396 0 : list_order = INT32_MIN;
397 : }
398 0 : else if ( x >= draw_right )
399 : {
400 0 : list_order = INT32_MAX;
401 : }
402 : else
403 : {
404 0 : list_order = ((uint32_t)(( x - draw_left ) / ( draw_right - draw_left ) * UINT32_MAX));
405 0 : list_order += INT32_MIN;
406 : }
407 0 : layout_order_init_list( out_layout_order, list_order );
408 : }
409 0 : else if ((( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )&& scenario_semantics)
410 0 : || ( DATA_DIAGRAM_TYPE_LIST == diag_type )
411 0 : || (( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type)&&( ! scenario_semantics)))
412 0 : {
413 : /* classifiers are a vertical list */
414 0 : const double draw_top = geometry_rectangle_get_top(diagram_draw_area);
415 0 : const double draw_bottom = geometry_rectangle_get_bottom(diagram_draw_area);
416 : int32_t list_order;
417 0 : if ( y <= draw_top )
418 : {
419 0 : list_order = INT32_MIN;
420 : }
421 0 : else if ( y >= draw_bottom )
422 : {
423 0 : list_order = INT32_MAX;
424 : }
425 : else
426 : {
427 0 : list_order = ((uint32_t)(( y - draw_top ) / ( draw_bottom - draw_top ) * UINT32_MAX));
428 0 : list_order += INT32_MIN;
429 : }
430 0 : layout_order_init_list( out_layout_order, list_order );
431 : }
432 : else
433 : {
434 : /* classifiers are x/y arranged */
435 : const geometry_non_linear_scale_t *const x_scale
436 0 : = geometry_grid_get_x_scale_const( &((*this_).grid) );
437 : const geometry_non_linear_scale_t *const y_scale
438 0 : = geometry_grid_get_y_scale_const( &((*this_).grid) );
439 0 : const int32_t x_order = geometry_non_linear_scale_get_order( x_scale, x, snap_distance );
440 0 : const int32_t y_order = geometry_non_linear_scale_get_order( y_scale, y, snap_distance );
441 0 : layout_order_init_x_y( out_layout_order, x_order, y_order );
442 : }
443 : }
444 :
445 0 : U8_TRACE_END_ERR( result );
446 0 : return result;
447 : }
448 :
449 0 : pencil_error_t pencil_layouter_get_feature_order_at_pos ( const pencil_layouter_t *this_,
450 : const data_feature_t *feature_ptr,
451 : double x,
452 : double y,
453 : layout_order_t* out_layout_order )
454 : {
455 0 : U8_TRACE_BEGIN();
456 0 : assert ( NULL != out_layout_order );
457 0 : assert ( NULL != feature_ptr );
458 :
459 0 : pencil_error_t result = PENCIL_ERROR_NONE;
460 :
461 : /* get data of feature */
462 : data_row_id_t parent_classifier_id;
463 : data_feature_type_t feature_type;
464 0 : feature_type = data_feature_get_main_type ( feature_ptr );
465 0 : parent_classifier_id = data_feature_get_classifier_row_id ( feature_ptr );
466 :
467 : /* get the bounding box of the diagram */
468 : const layout_diagram_t *const the_diagram
469 0 : = layout_visible_set_get_diagram_const( &((*this_).layout_data) );
470 : const geometry_rectangle_t *const diagram_bounds
471 0 : = layout_diagram_get_bounds_const( the_diagram );
472 :
473 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
474 : {
475 0 : layout_order_init_empty( out_layout_order );
476 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
477 : }
478 0 : else if ( DATA_ROW_ID_VOID == parent_classifier_id ) {
479 0 : U8_LOG_WARNING( "feature to move has no parent classifier!" );
480 0 : layout_order_init_empty( out_layout_order );
481 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
482 : }
483 : else
484 : {
485 : /* iterate over all classifiers, search the closest_parent_instance */
486 0 : const layout_visible_classifier_t *closest_parent_instance = NULL;
487 : const uint32_t classfy_count
488 0 : = layout_visible_set_get_visible_classifier_count ( &((*this_).layout_data) );
489 0 : for ( uint32_t classfy_index = 0; classfy_index < classfy_count; classfy_index ++ )
490 : {
491 : const layout_visible_classifier_t *const visible_classifier
492 0 : = layout_visible_set_get_visible_classifier_const ( &((*this_).layout_data), classfy_index );
493 : const data_row_id_t classfy_id
494 0 : = layout_visible_classifier_get_classifier_id ( visible_classifier );
495 0 : if ( parent_classifier_id == classfy_id )
496 : {
497 0 : if ( NULL == closest_parent_instance )
498 : {
499 0 : closest_parent_instance = visible_classifier;
500 : }
501 : else
502 : {
503 : const geometry_rectangle_t *const classfier_symbol_box
504 0 : = layout_visible_classifier_get_symbol_box_const ( visible_classifier );
505 : const geometry_rectangle_t *const closest_parent_symbol_box
506 0 : = layout_visible_classifier_get_symbol_box_const ( closest_parent_instance );
507 0 : const double classfy_distance = geometry_rectangle_calc_chess_distance( classfier_symbol_box, x, y );
508 : const double closest_parent_distance
509 0 : = geometry_rectangle_calc_chess_distance( closest_parent_symbol_box, x, y );
510 0 : if ( classfy_distance < closest_parent_distance )
511 : {
512 0 : closest_parent_instance = visible_classifier;
513 : }
514 : }
515 : }
516 : }
517 :
518 0 : if ( NULL != closest_parent_instance )
519 : {
520 0 : switch (feature_type)
521 : {
522 0 : case DATA_FEATURE_TYPE_PROPERTY: /* or */
523 : case DATA_FEATURE_TYPE_OPERATION: /* or */
524 : case DATA_FEATURE_TYPE_TAGGED_VALUE: /* or */
525 : default: /* this may happen if a new database file has been read by an old program version */
526 : {
527 0 : int32_t max_order_above = INT32_MIN;
528 0 : int32_t min_order_below = INT32_MAX;
529 : /* iterate over all contained features */
530 0 : const uint32_t f_count = layout_visible_set_get_feature_count( &((*this_).layout_data) );
531 0 : for ( uint32_t f_idx = 0; f_idx < f_count; f_idx ++ )
532 : {
533 : /* check if feature belongs to same parent classifier */
534 : const layout_feature_t *const the_feature
535 0 : = layout_visible_set_get_feature_const ( &((*this_).layout_data), f_idx );
536 : const layout_visible_classifier_t *const vis_classfy
537 0 : = layout_feature_get_classifier_const ( the_feature );
538 0 : if ( closest_parent_instance == vis_classfy )
539 : {
540 : /* check if feature is not the moved one */
541 : const data_feature_t *const data_feature
542 0 : = layout_feature_get_data_const ( the_feature );
543 0 : if ( data_feature_get_row_id ( feature_ptr ) != data_feature_get_row_id ( data_feature ) )
544 : {
545 : const int32_t list_order
546 0 : = data_feature_get_list_order( data_feature );
547 : const geometry_rectangle_t *const feature_symbol_box
548 0 : = layout_feature_get_symbol_box_const ( the_feature );
549 0 : if ( y < geometry_rectangle_get_center_y( feature_symbol_box ) )
550 : {
551 0 : if ( list_order < min_order_below ) { min_order_below = list_order; }
552 : }
553 : else
554 : {
555 0 : if ( list_order > max_order_above ) { max_order_above = list_order; }
556 : }
557 : }
558 : }
559 : }
560 :
561 0 : if ( max_order_above == INT32_MIN )
562 : {
563 0 : if ( min_order_below == INT32_MAX )
564 : {
565 : /* nothing above, nothing below */
566 0 : layout_order_init_list( out_layout_order, 0 );
567 : }
568 : else
569 : {
570 : /* nothing above */
571 0 : layout_order_init_list( out_layout_order, min_order_below - 32768 );
572 : }
573 : }
574 : else
575 : {
576 0 : if ( min_order_below == INT32_MAX )
577 : {
578 : /* nothing below */
579 0 : layout_order_init_list( out_layout_order, max_order_above + 32768 );
580 : }
581 : else
582 : {
583 : /* regular interval */
584 0 : layout_order_init_list( out_layout_order, (max_order_above + min_order_below)/2 );
585 : }
586 : }
587 : }
588 0 : break;
589 :
590 0 : case DATA_FEATURE_TYPE_PORT: /* or */
591 : case DATA_FEATURE_TYPE_PROVIDED_INTERFACE: /* or */
592 : case DATA_FEATURE_TYPE_REQUIRED_INTERFACE: /* or */
593 : case DATA_FEATURE_TYPE_IN_PORT_PIN: /* or */
594 : case DATA_FEATURE_TYPE_OUT_PORT_PIN: /* or */
595 : case DATA_FEATURE_TYPE_ENTRY: /* or */
596 : case DATA_FEATURE_TYPE_EXIT:
597 : {
598 : const geometry_rectangle_t *const closest_parent_symbol_box
599 0 : = layout_visible_classifier_get_symbol_box_const ( closest_parent_instance );
600 0 : const double center_x = geometry_rectangle_get_center_x( closest_parent_symbol_box );
601 0 : const double center_y = geometry_rectangle_get_center_y( closest_parent_symbol_box );
602 0 : const double width = geometry_rectangle_get_width( closest_parent_symbol_box );
603 0 : const double height = geometry_rectangle_get_height( closest_parent_symbol_box );
604 0 : const double delta_x = x - center_x;
605 0 : const double delty_y = y - center_y;
606 0 : const double relative_delta_x = delta_x * height;
607 0 : const double relative_delta_y = delty_y * width;
608 0 : const double distance_x = ( x < center_x ) ? (center_x - x) : (x - center_x);
609 0 : const double distance_y = ( y < center_y ) ? (center_y - y) : (y - center_y);
610 0 : const double relative_dist_x = distance_x * height;
611 0 : const double relative_dist_y = distance_y * width;
612 : int32_t order;
613 0 : if ( relative_dist_x > relative_dist_y )
614 : {
615 0 : if ( x < center_x )
616 : {
617 : /* x,y is on left side, order is between 0 and INT32_MAX/2 */
618 0 : order = INT32_MAX*(relative_delta_y/(relative_dist_x+0.1)*0.25+0.25);
619 : }
620 : else
621 : {
622 : /* x,y is on right side, order is between INT32_MIN and INT32_MIN/2 */
623 0 : order = INT32_MIN*(relative_delta_y/(relative_dist_x+0.1)*0.25+0.75);
624 : }
625 : }
626 : else
627 : {
628 0 : if ( y < center_y )
629 : {
630 : /* x,y is on upper side, order is between INT32_MIN/2 and 0 */
631 0 : order = INT32_MIN*(relative_delta_x/(relative_dist_y+0.1)*0.25+0.25);
632 : }
633 : else
634 : {
635 : /* x,y is on lower side, order is between INT32_MAX/2 and INT32_MAX */
636 0 : order = INT32_MAX*(relative_delta_x/(relative_dist_y+0.1)*0.25+0.75);
637 : }
638 : }
639 0 : layout_order_init_list( out_layout_order, order );
640 : }
641 0 : break;
642 :
643 0 : case DATA_FEATURE_TYPE_LIFELINE:
644 : {
645 0 : U8_TRACE_INFO( "feature to move is a lifeline and therefore cannot move." );
646 0 : layout_order_init_empty( out_layout_order );
647 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
648 : }
649 0 : break;
650 : }
651 : }
652 : else
653 : {
654 0 : U8_LOG_WARNING( "parent classifier of feature is not visible; possibly array size too small?" );
655 0 : layout_order_init_empty( out_layout_order );
656 0 : result = PENCIL_ERROR_UNKNOWN_OBJECT;
657 : }
658 : }
659 :
660 0 : U8_TRACE_END_ERR( result );
661 0 : return result;
662 : }
663 :
664 0 : pencil_error_t pencil_layouter_get_relationship_order_at_pos ( const pencil_layouter_t *this_,
665 : double x,
666 : double y,
667 : layout_order_t* out_layout_order )
668 : {
669 0 : U8_TRACE_BEGIN();
670 0 : assert ( NULL != out_layout_order );
671 :
672 0 : pencil_error_t result = PENCIL_ERROR_NONE;
673 :
674 : /* get the bounding box of the diagram */
675 : const layout_diagram_t *const the_diagram
676 0 : = layout_visible_set_get_diagram_const( &((*this_).layout_data) );
677 : const geometry_rectangle_t *const diagram_bounds
678 0 : = layout_diagram_get_bounds_const( the_diagram );
679 : const geometry_rectangle_t *const diagram_draw_area
680 0 : = layout_diagram_get_draw_area_const( the_diagram );
681 :
682 : /* get the diagram type */
683 : const data_diagram_t *const diagram_data
684 0 : = layout_diagram_get_data_const ( the_diagram );
685 : const data_diagram_type_t diag_type
686 0 : = data_diagram_get_diagram_type ( diagram_data );
687 :
688 0 : if ( ! geometry_rectangle_contains( diagram_bounds, x, y ) )
689 : {
690 0 : layout_order_init_empty( out_layout_order );
691 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
692 : }
693 : else
694 : {
695 0 : if (( DATA_DIAGRAM_TYPE_BOX_DIAGRAM == diag_type )
696 0 : || ( DATA_DIAGRAM_TYPE_LIST == diag_type ))
697 : {
698 : /* relationships are hidden in lists and box-diagrams */
699 0 : layout_order_init_empty( out_layout_order );
700 0 : result = PENCIL_ERROR_OUT_OF_BOUNDS;
701 : }
702 0 : else if (( DATA_DIAGRAM_TYPE_UML_COMMUNICATION_DIAGRAM == diag_type )||( DATA_DIAGRAM_TYPE_INTERACTION_OVERVIEW_DIAGRAM == diag_type ))
703 : {
704 : /* communication diagrams and interaction overview diagrams do not care about list_orders of relationships */
705 0 : layout_order_init_empty( out_layout_order );
706 : }
707 0 : else if ( DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM == diag_type )
708 : {
709 0 : const double draw_top = geometry_rectangle_get_top(diagram_draw_area);
710 0 : const double draw_bottom = geometry_rectangle_get_bottom(diagram_draw_area);
711 : int32_t list_order;
712 0 : if ( y <= draw_top )
713 : {
714 0 : list_order = INT32_MIN;
715 : }
716 0 : else if ( y >= draw_bottom )
717 : {
718 0 : list_order = INT32_MAX;
719 : }
720 : else
721 : {
722 0 : list_order = ((uint32_t)(( y - draw_top ) / ( draw_bottom - draw_top ) * UINT32_MAX));
723 0 : list_order += INT32_MIN;
724 : }
725 0 : layout_order_init_list( out_layout_order, list_order );
726 : }
727 0 : else if ( DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM == diag_type )
728 : {
729 0 : const double draw_left = geometry_rectangle_get_left(diagram_draw_area);
730 0 : const double draw_right = geometry_rectangle_get_right(diagram_draw_area);
731 : int32_t list_order;
732 0 : if ( x <= draw_left )
733 : {
734 0 : list_order = INT32_MIN;
735 : }
736 0 : else if ( x >= draw_right )
737 : {
738 0 : list_order = INT32_MAX;
739 : }
740 : else
741 : {
742 0 : list_order = ((uint32_t)(( x - draw_left ) / ( draw_right - draw_left ) * UINT32_MAX));
743 0 : list_order += INT32_MIN;
744 : }
745 0 : layout_order_init_list( out_layout_order, list_order );
746 : }
747 : else
748 : {
749 : /* all other diagram types do not care about list_orders of relationships */
750 0 : layout_order_init_empty( out_layout_order );
751 : }
752 : }
753 :
754 0 : U8_TRACE_END_ERR( result );
755 0 : return result;
756 : }
757 :
758 :
759 : /*
760 : Copyright 2017-2024 Andreas Warnke
761 :
762 : Licensed under the Apache License, Version 2.0 (the "License");
763 : you may not use this file except in compliance with the License.
764 : You may obtain a copy of the License at
765 :
766 : http://www.apache.org/licenses/LICENSE-2.0
767 :
768 : Unless required by applicable law or agreed to in writing, software
769 : distributed under the License is distributed on an "AS IS" BASIS,
770 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
771 : See the License for the specific language governing permissions and
772 : limitations under the License.
773 : */
|