LineArt: Better behavior of smooth tolerance.

This fixes the smooth tolerance feature in master where sometimes you could
get over simplified chains and lose the shape it's supposed to be originally.

Reviewed By: Sebastian Parborg (zeddb)

Differential Revision: https://developer.blender.org/D14929
This commit is contained in:
YimingWu 2022-05-14 00:55:18 +08:00
parent ee363ee7b3
commit 7c9b6cc380
1 changed files with 58 additions and 18 deletions

View File

@ -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;
/* 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;
/* 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;
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 accomondating the entire
* scene. Those situations typically result in (ratio << 0), looks like this:
* 1---2
* 3-------------------------------4
* (this sort of long zig zag obviously are "features" that can't be erased)
* setting a ratio of -10 turned out to be a reasonabe 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);
}
}
}