diff options
Diffstat (limited to 'source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c')
-rw-r--r-- | source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index b666eb677eb..28fd8ff01b9 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -971,27 +971,67 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { - LineartEdgeChainItem *next_eci; - for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { - next_eci = eci->next; - LineartEdgeChainItem *eci2, *eci3, *eci4; - - /* Not enough point to do simplify. */ - if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { - continue; - } - - /* No need to care for different line types/occlusion and so on, because at this stage they - * are all the same within a chain. */ - - /* If p3 is within the p1-p2 segment of a width of "tolerance". */ - if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { - /* And if p4 is on the extension of p1-p2 , we remove p3. */ - if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { - BLI_remlink(&ec->chain, eci3); - next_eci = eci; + /* Go through the chain two times, once from each direction. */ + for (int times = 0; times < 2; times++) { + for (LineartEdgeChainItem *eci = ec->chain.first, *next_eci = eci->next; eci; + eci = next_eci) { + LineartEdgeChainItem *eci2, *eci3, *eci4; + + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + /* Not enough points to simplify. */ + next_eci = eci->next; + continue; } + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. + * + * We need to simplify a chain from this: + * 1-----------2 + * 3-----------4 + * to this: + * 1-----------2--_ + * `--4 */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance", in other words, p3 is + * approximately on the segment of p1-p2. */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + float vec2[2], vec3[2], v2n[2], ratio, len2; + sub_v2_v2v2(vec2, eci2->pos, eci->pos); + sub_v2_v2v2(vec3, eci3->pos, eci->pos); + normalize_v2_v2(v2n, vec2); + ratio = dot_v2v2(v2n, vec3); + len2 = len_v2(vec2); + /* Because this smoothing applies on geometries of different scales in the same scene, + * some small scale features (e.g. the "tails" on the inner ring of a torus geometry) + * could be completely erased if the tolerance value is set for accommodating the entire + * scene. Those situations typically result in (ratio << 0), looks like this: + * 1---2 + * 3-------------------------------4 + * (this sort of long zigzag obviously are "features" that can't be erased) + * setting a ratio of -10 turned out to be a reasonable threshold in tests. */ + if (ratio < len2 && ratio > -len2 * 10) { + /* We only remove p3 if p4 is on the extension of p1->p2. */ + if ((eci4 = eci3->next) && + (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&ec->chain, eci3); + next_eci = eci; + continue; + } + if (!eci4) { + /* See if the last segment's direction is reversed, if so remove that. + * Basically we don't need to preserve p3 if the entire chain looked like this: + * ...----1----3===2 */ + if (len_v2(vec2) > len_v2(vec3)) { + BLI_remlink(&ec->chain, eci3); + } + next_eci = NULL; + continue; + } + } + } + next_eci = eci->next; } + BLI_listbase_reverse(&ec->chain); } } } |