comparison src/redraw_man.c @ 16:e17e12b112c4

A simple animation using rdman_redraw_changed().
author Thinker K.F. Li <thinker@branda.to>
date Fri, 01 Aug 2008 18:20:28 +0800
parents c2ce186a5c37
children 41f0907b27ac
comparison
equal deleted inserted replaced
15:c2ce186a5c37 16:e17e12b112c4
10 #define OK 0 10 #define OK 0
11 #define ERR -1 11 #define ERR -1
12 12
13 #define OFFSET(type, field) ((void *)&((type *)NULL)->field - (void *)NULL) 13 #define OFFSET(type, field) ((void *)&((type *)NULL)->field - (void *)NULL)
14 #define SWAP(a, b, t) do { t c; c = a; a = b; b = c; } while(0) 14 #define SWAP(a, b, t) do { t c; c = a; a = b; b = c; } while(0)
15
16 /*! \brief Sort a list of element by a unsigned integer.
17 *
18 * The result is in ascend order. The unsigned integers is
19 * at offset specified by 'off' from start address of elemnts.
20 */
21 static void _insert_sort(void **elms, int num, int off) {
22 int i, j;
23 unsigned int val;
24
25 for(i = 1; i < num; i++) {
26 val = *(unsigned int *)(elms[i] + off);
27 for(j = i; j > 0; j--) {
28 if(*(unsigned int *)(elms[j - 1] + off) <= val)
29 break;
30 elms[j] = elms[j - 1];
31 }
32 elms[j] = elms[i];
33 }
34 }
35
36 static int extend_memblk(void **buf, int o_size, int n_size) {
37 void *new_buf;
38
39 new_buf = realloc(*buf, n_size);
40 if(new_buf == NULL)
41 return ERR;
42
43 if(new_buf != *buf)
44 *buf = new_buf;
45
46 return OK;
47 }
48
49 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
50 int max_dirty_geos;
51 int r;
52
53 if(rdman->n_dirty_geos >= rdman->max_dirty_geos) {
54 max_dirty_geos = rdman->n_geos;
55 r = extend_memblk((void **)&rdman->dirty_geos,
56 sizeof(geo_t *) * rdman->n_dirty_geos,
57 sizeof(geo_t *) * max_dirty_geos);
58 if(r != OK)
59 return ERR;
60 rdman->max_dirty_geos = max_dirty_geos;
61 }
62
63 rdman->dirty_geos[rdman->n_dirty_geos++] = geo;
64 return OK;
65 }
66
67 static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
68 int max_dirty_areas;
69 int r;
70
71 if(rdman->n_dirty_areas >= rdman->max_dirty_areas) {
72 /* every geo object and coord object can contribute 2 areas. */
73 max_dirty_areas = (rdman->n_geos + rdman->n_coords) * 2;
74 r = extend_memblk((void **)&rdman->dirty_areas,
75 sizeof(area_t *) * rdman->n_dirty_areas,
76 sizeof(area_t *) * max_dirty_areas);
77 if(r != OK)
78 return ERR;
79 rdman->max_dirty_areas = max_dirty_areas;
80 }
81
82 rdman->dirty_areas[rdman->n_dirty_areas++] = area;
83 return OK;
84 }
15 85
16 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr) { 86 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr) {
17 extern void redraw_man_destroy(redraw_man_t *rdman); 87 extern void redraw_man_destroy(redraw_man_t *rdman);
18 88
19 memset(rdman, 0, sizeof(redraw_man_t)); 89 memset(rdman, 0, sizeof(redraw_man_t));
114 return n_overlays; 184 return n_overlays;
115 } 185 }
116 186
117 int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) { 187 int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) {
118 geo_t *geo; 188 geo_t *geo;
189 #ifdef GEO_ORDER
119 geo_t *visit; 190 geo_t *visit;
120 unsigned int next_order; 191 unsigned int next_order;
192 #endif
193 int r;
121 194
122 geo = elmpool_elm_alloc(rdman->geo_pool); 195 geo = elmpool_elm_alloc(rdman->geo_pool);
123 if(geo == NULL) 196 if(geo == NULL)
124 return ERR; 197 return ERR;
125 sh_attach_geo(shape, geo); 198 sh_attach_geo(shape, geo);
126 STAILQ_INS_TAIL(rdman->all_geos, geo_t, next, geo); 199 STAILQ_INS_TAIL(rdman->all_geos, geo_t, next, geo);
127 rdman->n_geos++; 200 rdman->n_geos++;
128 201
202 #ifdef GEO_ORDER
203 /* TODO: remove order number. */
129 geo->order = ++rdman->next_geo_order; 204 geo->order = ++rdman->next_geo_order;
130 if(geo->order == 0) { 205 if(geo->order == 0) {
131 next_order = 0; 206 next_order = 0;
132 for(visit = STAILQ_HEAD(rdman->all_geos); 207 for(visit = STAILQ_HEAD(rdman->all_geos);
133 visit != NULL; 208 visit != NULL;
134 visit = STAILQ_NEXT(geo_t, next, visit)) 209 visit = STAILQ_NEXT(geo_t, next, visit))
135 visit->order = ++next_order; 210 visit->order = ++next_order;
136 rdman->next_geo_order = next_order; 211 rdman->next_geo_order = next_order;
137 } 212 }
213 #endif
214
215 /* New one should be dirty to recompute it when drawing. */
138 geo->flags |= GEF_DIRTY; 216 geo->flags |= GEF_DIRTY;
217 r = add_dirty_geo(rdman, geo);
218 if(r != OK)
219 return ERR;
139 220
140 sh_attach_coord(shape, coord); 221 sh_attach_coord(shape, coord);
141 222
142 return OK; 223 return OK;
143 } 224 }
200 rdman->n_coords--; 281 rdman->n_coords--;
201 282
202 return OK; 283 return OK;
203 } 284 }
204 285
205 static int extend_memblk(void **buf, int o_size, int n_size) {
206 void *new_buf;
207
208 new_buf = realloc(*buf, n_size);
209 if(new_buf == NULL)
210 return ERR;
211
212 if(new_buf != *buf) {
213 memcpy(new_buf, *buf, o_size);
214 free(*buf);
215 *buf = new_buf;
216 }
217
218 return OK;
219 }
220
221 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
222 int max_dirty_geos;
223 int r;
224
225 if(rdman->n_dirty_geos >= rdman->max_dirty_geos) {
226 max_dirty_geos = rdman->n_geos;
227 r = extend_memblk((void **)&rdman->dirty_geos,
228 sizeof(geo_t *) * rdman->n_dirty_geos,
229 sizeof(geo_t *) * max_dirty_geos);
230 if(r != OK)
231 return ERR;
232 rdman->max_dirty_geos = max_dirty_geos;
233 }
234
235 rdman->dirty_geos[rdman->n_dirty_geos++] = geo;
236 return OK;
237 }
238
239 static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
240 int max_dirty_areas;
241 int r;
242
243 if(rdman->n_dirty_areas >= rdman->max_dirty_areas) {
244 /* every geo object and coord object can contribute 2 areas. */
245 max_dirty_areas = (rdman->n_geos + rdman->n_coords) * 2;
246 r = extend_memblk((void **)&rdman->dirty_areas,
247 sizeof(area_t *) * rdman->n_dirty_areas,
248 sizeof(area_t *) * max_dirty_areas);
249 if(r != OK)
250 return ERR;
251 rdman->max_dirty_areas = max_dirty_areas;
252 }
253
254 rdman->dirty_areas[++rdman->n_dirty_areas] = area;
255 return OK;
256 }
257
258 /*! \brief Mark a coord is changed. 286 /*! \brief Mark a coord is changed.
259 * 287 *
260 * A changed coord_t object is marked as dirty and put 288 * A changed coord_t object is marked as dirty and put
261 * into dirty_coords list. 289 * into dirty_coords list.
262 */ 290 */
263 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) { 291 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
292 coord_t *child;
264 int max_dirty_coords; 293 int max_dirty_coords;
265 int r; 294 int r;
266 295
267 if(coord->flags & COF_DIRTY) 296 if(coord->flags & COF_DIRTY)
268 return OK; 297 return OK;
275 sizeof(coord_t *) * rdman->n_dirty_coords, 304 sizeof(coord_t *) * rdman->n_dirty_coords,
276 sizeof(coord_t *) * max_dirty_coords); 305 sizeof(coord_t *) * max_dirty_coords);
277 rdman->max_dirty_coords = max_dirty_coords; 306 rdman->max_dirty_coords = max_dirty_coords;
278 } 307 }
279 308
280 rdman->dirty_coords[rdman->n_dirty_coords++] = coord; 309 /* Make the coord and child coords dirty. */
281 coord->flags |= COF_DIRTY; 310 for(child = coord;
311 child != NULL;
312 child = preorder_coord_subtree(coord, child)) {
313 rdman->dirty_coords[rdman->n_dirty_coords++] = coord;
314 coord->flags |= COF_DIRTY;
315 }
282 316
283 return OK; 317 return OK;
284 } 318 }
285 319
286 /*! \brief Mark a shape is changed. 320 /*! \brief Mark a shape is changed.
303 geo->flags |= GEF_DIRTY; 337 geo->flags |= GEF_DIRTY;
304 338
305 return OK; 339 return OK;
306 } 340 }
307 341
308 /*! \brief Sort a list of element by a unsigned integer. 342 static void clean_shape(shape_t *shape) {
309 *
310 * The result is in ascend order. The unsigned integers is
311 * at offset specified by 'off' from start address of elemnts.
312 */
313 static void _insert_sort(void **elms, int num, int off) {
314 int i, j;
315 unsigned int val;
316
317 for(i = 1; i < num; i++) {
318 val = *(unsigned int *)(elms[i] + off);
319 for(j = i; j > 0; j--) {
320 if(*(unsigned int *)(elms[j - 1] + off) <= val)
321 break;
322 elms[j] = elms[j - 1];
323 }
324 elms[j] = elms[i];
325 }
326 }
327
328 static void update_shape_geo(shape_t *shape) {
329 switch(shape->sh_type) { 343 switch(shape->sh_type) {
330 case SHT_PATH: 344 case SHT_PATH:
331 sh_path_transform(shape); 345 sh_path_transform(shape);
332 break; 346 break;
333 } 347 }
348 shape->geo->flags &= ~GEF_DIRTY;
334 } 349 }
335 350
336 static void area_to_positions(area_t *area, co_aix (*poses)[2]) { 351 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
337 poses[0][0] = area->x; 352 poses[0][0] = area->x;
338 poses[0][1] = area->y; 353 poses[0][1] = area->y;
339 poses[1][0] = area->x + area->w; 354 poses[1][0] = area->x + area->w;
340 poses[1][1] = area->y + area->h;; 355 poses[1][1] = area->y + area->h;;
341 } 356 }
342 357
343 static int compute_coord_area(coord_t *coord) { 358 static int clean_coord(coord_t *coord) {
344 shape_t *shape; 359 shape_t *shape;
345 geo_t *geo; 360 geo_t *geo;
346 co_aix (*poses)[2]; 361 co_aix (*poses)[2];
347 int cnt, pos_cnt; 362 int cnt, pos_cnt;
348 int i; 363 int i;
349 364
365 compute_aggr_of_coord(coord);
366
367 /* Clean member shapes. */
350 cnt = 0; 368 cnt = 0;
351 for(shape = STAILQ_HEAD(coord->members); 369 for(shape = STAILQ_HEAD(coord->members);
352 shape != NULL; 370 shape != NULL;
353 shape = STAILQ_NEXT(shape_t, coord_mem_next, shape)) { 371 shape = STAILQ_NEXT(shape_t, coord_mem_next, shape)) {
354 SWAP(shape->geo->cur_area, shape->geo->last_area, area_t *); 372 geo = shape->geo;
355 update_shape_geo(shape); 373 SWAP(geo->cur_area, geo->last_area, area_t *);
374 clean_shape(shape);
356 cnt++; 375 cnt++;
357 } 376 }
358 377
378 /* Compute area of the coord. */
359 poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * 2 * cnt); 379 poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * 2 * cnt);
360 if(poses == NULL) 380 if(poses == NULL)
361 return ERR; 381 return ERR;
362 382
363 pos_cnt = 0; 383 pos_cnt = 0;
370 pos_cnt += 2; 390 pos_cnt += 2;
371 area_to_positions(&geo->areas[1], poses + pos_cnt); 391 area_to_positions(&geo->areas[1], poses + pos_cnt);
372 pos_cnt += 2; 392 pos_cnt += 2;
373 } 393 }
374 394
395 #if 0
375 for(i = 0; i < pos_cnt; i++) 396 for(i = 0; i < pos_cnt; i++)
376 coord_trans_pos(coord, &poses[i][0], &poses[i][1]); 397 coord_trans_pos(coord, &poses[i][0], &poses[i][1]);
377 398 #endif
378 area_init(coord->cur_area, cnt, poses); 399
400 SWAP(coord->cur_area, coord->last_area, area_t *);
401 area_init(coord->cur_area, pos_cnt, poses);
379 free(poses); 402 free(poses);
380 403
404 coord->flags &= ~COF_DIRTY;
405
406 return OK;
407 }
408
409 static int clean_rdman_coords(redraw_man_t *rdman) {
410 coord_t *coord;
411 coord_t **dirty_coords;
412 int n_dirty_coords;
413 int i, r;
414
415 n_dirty_coords = rdman->n_dirty_coords;
416 if(n_dirty_coords > 0) {
417 dirty_coords = rdman->dirty_coords;
418 _insert_sort((void **)dirty_coords, n_dirty_coords,
419 OFFSET(coord_t, order));
420 for(i = 0; i < n_dirty_coords; i++) {
421 coord = dirty_coords[i];
422 if(!(coord->flags & COF_DIRTY))
423 continue;
424 r = clean_coord(coord);
425 if(r != OK)
426 return ERR;
427 /* These two steps can be avoided for drawing all. */
428 add_dirty_area(rdman, &coord->areas[0]);
429 add_dirty_area(rdman, &coord->areas[1]);
430 }
431 rdman->n_dirty_coords = 0;
432 }
381 return OK; 433 return OK;
382 } 434 }
383 435
384 static void draw_shape(redraw_man_t *rdman, shape_t *shape) { 436 static void draw_shape(redraw_man_t *rdman, shape_t *shape) {
385 switch(shape->sh_type) { 437 switch(shape->sh_type) {
387 sh_path_draw(shape, rdman->cr); 439 sh_path_draw(shape, rdman->cr);
388 break; 440 break;
389 } 441 }
390 } 442 }
391 443
444 static void clean_clip(cairo_t *cr) {
445 cairo_pattern_t *pt;
446
447 pt = cairo_get_source(cr);
448 cairo_pattern_reference(pt);
449 /* TODO: clean to background color. */
450 cairo_set_source_rgb(cr, 0, 0, 0);
451 cairo_fill_preserve(cr);
452 cairo_set_source(cr, pt);
453 cairo_pattern_destroy(pt);
454 }
455
392 static void make_clip(redraw_man_t *rdman, int n_dirty_areas, 456 static void make_clip(redraw_man_t *rdman, int n_dirty_areas,
393 area_t **dirty_areas) { 457 area_t **dirty_areas) {
394 int i; 458 int i;
395 area_t *area; 459 area_t *area;
396 cairo_t *cr; 460 cairo_t *cr;
400 cairo_reset_clip(cr); 464 cairo_reset_clip(cr);
401 for(i = 0; i < n_dirty_areas; i++) { 465 for(i = 0; i < n_dirty_areas; i++) {
402 area = dirty_areas[i]; 466 area = dirty_areas[i];
403 cairo_rectangle(cr, area->x, area->y, area->w, area->h); 467 cairo_rectangle(cr, area->x, area->y, area->w, area->h);
404 } 468 }
469 clean_clip(cr);
405 cairo_clip(cr); 470 cairo_clip(cr);
406 } 471 }
407 472
408 static void draw_shapes_in_areas(redraw_man_t *rdman, 473 static void draw_shapes_in_areas(redraw_man_t *rdman,
409 int n_areas, 474 int n_areas,
412 int i; 477 int i;
413 478
414 for(visit_geo = STAILQ_HEAD(rdman->all_geos); 479 for(visit_geo = STAILQ_HEAD(rdman->all_geos);
415 visit_geo != NULL; 480 visit_geo != NULL;
416 visit_geo = STAILQ_NEXT(geo_t, next, visit_geo)) { 481 visit_geo = STAILQ_NEXT(geo_t, next, visit_geo)) {
417 if(visit_geo->flags & GEF_DIRTY) { 482 if(visit_geo->flags & GEF_DIRTY)
418 visit_geo->flags &= ~GEF_DIRTY; 483 clean_shape(visit_geo->shape);
419 update_shape_geo(visit_geo->shape);
420 }
421 for(i = 0; i < n_areas; i++) { 484 for(i = 0; i < n_areas; i++) {
422 if(is_overlay(visit_geo->cur_area, areas[i])) { 485 if(is_overlay(visit_geo->cur_area, areas[i])) {
423 draw_shape(rdman, visit_geo->shape); 486 draw_shape(rdman, visit_geo->shape);
424 break; 487 break;
425 } 488 }
459 * corod objects. 522 * corod objects.
460 * 523 *
461 */ 524 */
462 int rdman_redraw_changed(redraw_man_t *rdman) { 525 int rdman_redraw_changed(redraw_man_t *rdman) {
463 int i, r; 526 int i, r;
464 int n_dirty_coords;
465 coord_t **dirty_coords;
466 coord_t *visit_coord;
467 geo_t *visit_geo, **dirty_geos; 527 geo_t *visit_geo, **dirty_geos;
468 int n_dirty_geos; 528 int n_dirty_geos;
469 int n_dirty_areas; 529 int n_dirty_areas;
470 area_t **dirty_areas; 530 area_t **dirty_areas;
471 531
472 if(rdman->n_dirty_coords > 0) { 532 r = clean_rdman_coords(rdman);
473 _insert_sort((void **)rdman->dirty_coords, 533 if(r != OK)
474 rdman->n_dirty_coords, 534 return ERR;
475 OFFSET(coord_t, order));
476 n_dirty_coords = rdman->n_dirty_coords;
477 dirty_coords = rdman->dirty_coords;
478 for(i = 0; i < n_dirty_coords; i++) {
479 if(!(dirty_coords[i]->flags & COF_DIRTY))
480 continue;
481
482 update_aggr_matrix(dirty_coords[i]);
483 for(visit_coord = dirty_coords[i];
484 visit_coord != NULL;
485 visit_coord = preorder_coord_subtree(dirty_coords[i],
486 visit_coord)) {
487 /* Dirty member, here, and members of this coord
488 * will not be visited anymore. */
489 visit_coord->flags &= ~COF_DIRTY;
490
491 SWAP(visit_coord->cur_area, visit_coord->last_area, area_t *);
492 r = compute_coord_area(visit_coord);
493 if(r == ERR)
494 return ERR;
495 add_dirty_area(rdman, visit_coord->cur_area);
496 add_dirty_area(rdman, visit_coord->last_area);
497 }
498 }
499 rdman->n_dirty_coords = 0;
500 }
501 535
502 n_dirty_geos = rdman->n_dirty_geos; 536 n_dirty_geos = rdman->n_dirty_geos;
503 if(n_dirty_geos > 0) { 537 if(n_dirty_geos > 0) {
504 dirty_geos = rdman->dirty_geos; 538 dirty_geos = rdman->dirty_geos;
505 for(i = 0; i < n_dirty_geos; i++) { 539 for(i = 0; i < n_dirty_geos; i++) {
506 visit_geo = dirty_geos[i]; 540 visit_geo = dirty_geos[i];
507 if(!(visit_geo->flags & GEF_DIRTY)) 541 if(!(visit_geo->flags & GEF_DIRTY))
508 continue; 542 continue;
509 543
510 visit_geo->flags &= ~GEF_DIRTY;
511 SWAP(visit_geo->cur_area, visit_geo->last_area, area_t *); 544 SWAP(visit_geo->cur_area, visit_geo->last_area, area_t *);
512 update_shape_geo(visit_geo->shape); 545 clean_shape(visit_geo->shape);
513 add_dirty_area(rdman, visit_geo->cur_area); 546 add_dirty_area(rdman, visit_geo->cur_area);
514 add_dirty_area(rdman, visit_geo->last_area); 547 add_dirty_area(rdman, visit_geo->last_area);
515 } 548 }
516 rdman->n_dirty_geos = 0; 549 rdman->n_dirty_geos = 0;
517 } 550 }
521 if(n_dirty_areas > 0) { 554 if(n_dirty_areas > 0) {
522 make_clip(rdman, n_dirty_areas, dirty_areas); 555 make_clip(rdman, n_dirty_areas, dirty_areas);
523 draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas); 556 draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas);
524 rdman->n_dirty_areas = 0; 557 rdman->n_dirty_areas = 0;
525 } 558 }
559 rdman->n_dirty_areas = 0;
560
561 cairo_reset_clip(rdman->cr);
526 562
527 return OK; 563 return OK;
528 } 564 }
529 565
530 int rdman_redraw_all(redraw_man_t *rdman) { 566 int rdman_redraw_all(redraw_man_t *rdman) {
531 geo_t *geo; 567 geo_t *geo;
532 568
533 /* TODO: update dirty coord and it's members. */ 569 clean_rdman_coords(rdman);
570 rdman->n_dirty_areas = 0;
571
572 cairo_reset_clip(rdman->cr);
534 for(geo = STAILQ_HEAD(rdman->all_geos); 573 for(geo = STAILQ_HEAD(rdman->all_geos);
535 geo != NULL; 574 geo != NULL;
536 geo = STAILQ_NEXT(geo_t, next, geo)) { 575 geo = STAILQ_NEXT(geo_t, next, geo)) {
537 if(geo->flags & GEF_DIRTY) { 576 if(geo->flags & GEF_DIRTY)
538 geo->flags &= ~GEF_DIRTY; 577 clean_shape(geo->shape);
539 update_shape_geo(geo->shape);
540 }
541 draw_shape(rdman, geo->shape); 578 draw_shape(rdman, geo->shape);
542 } 579 }
543 580 cairo_reset_clip(rdman->cr);
544 return OK; 581
545 } 582 return OK;
583 }
584
585 /*
586 * Dirty of geo
587 * A geo is dirty when any of the shape, size or positions is changed.
588 * It's geo and positions should be recomputed before drawing. So,
589 * dirty geos are marked as dirty and put into dirty_geos list.
590 * The list is inspected before drawing to make sure the right shape,
591 * size, and positions.
592 *
593 * Dirty of coord
594 * A coord is dirty when it's transformation matrix being changed.
595 * Dirty coords are marked as dirty and put into dirty_coords list.
596 * Once a coord is dirty, every member geos of it are also dirty.
597 * Because, their shape, size and positions will be changed. But,
598 * they are not marked as dirty and put into dirty_geos list, since
599 * all these member geos will be recomputed for computing new current
600 * area of the coord. The changes of a coord also affect child
601 * coords. Once parent is dirty, all children are also dirty for
602 * their aggregate matrix out of date. Dirty coords should be
603 * clean in preorder of tree traversal. The dirty_coords list
604 * are sorted to keep the order before cleaning.
605 * Whenever a coord is marked dirty and put into dirty_coords list,
606 * all it's children should also be marked and put.
607 *
608 * The procedure of clean coords comprises recomputing aggregate
609 * tranform matrix and area where members spreading in.
610 *
611 * The list is inspected before drawing to recompute new shape, size,
612 * and positions of member geos of coords in the list. The drity
613 * flag of member geos will be clean.
614 *
615 * Clean coords should be performed before clean geos, since clean
616 * coords will also clean member geos.
617 */
546 618
547 /* 619 /*
548 * When redraw an area, the affected elements may also extend to 620 * When redraw an area, the affected elements may also extend to
549 * outside of the area. Since the order of drawing will change 621 * outside of the area. Since the order of drawing will change
550 * the result, it will infect more and more elements to keep 622 * the result, it will infect more and more elements to keep