From d390d373a972f7de326f72d6a64da0e966b6a9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 19 May 2021 22:57:47 +0300 Subject: [PATCH] self-crossing bugfix --- notes.txt | 14 ++++++++++ tests.sql | 4 ++- wm.sql | 76 ++++++++++++++++++++++++++++++------------------------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/notes.txt b/notes.txt index f584c15..d7aa55b 100644 --- a/notes.txt +++ b/notes.txt @@ -52,6 +52,20 @@ To: But perhaps it doesn't look quite as natural. I will trust the original article to do the right thing here and remove the bend altogether. +ALSO: the bends should be iterated from different directions: + +for i := 0; i < len(bends); i++ { + for j := 0; j < i; j++ { + ... + } + for j := len(bends); j > i; j-- { + ... + } +} + +So if there are multiple bends between the baseline, they will be cut +correctly. + The Context of a Bend --------------------- diff --git a/tests.sql b/tests.sql index 8f54249..19fac6a 100644 --- a/tests.sql +++ b/tests.sql @@ -53,7 +53,9 @@ insert into wm_figures (name, way) values ('inflection-1',ST_GeomFromText('LINES insert into wm_figures (name, way) values ('multi-island',ST_GeomFromText('MULTILINESTRING((-15 10,-10 10,-5 11,0 11,5 11,10 10,11 9,13 10,15 9),(-5 11,-2 15,0 16,2 15,5 11))')); -- TODO: there is a bug and it does not go through `self_crossing` function. ---insert into wm_figures (name, way) values ('selfcrossing-1',ST_GeomFromText('LINESTRING(-27 180,-20 166,-21 142,-18 136,55 136,55 136,71 145,44 165,37 146,22 145,14 164,11 164,3 146,-12 146,-13 176,-18 184)')); +insert into wm_figures (name, way) values ('selfcrossing-1',ST_GeomFromText('LINESTRING(-27 180,-20 166,-21 142,-18 136,55 136,55 136,71 145,44 165,37 146,22 145,14 164,11 164,3 146,-12 146,-13 176,-18 184)')); +insert into wm_figures (name, way) values ('selfcrossing-1-rev',ST_Reverse(ST_Translate((select way from wm_figures where name='selfcrossing-1'), 60, 0))); + -- Run ST_SimplifyWM in debug mode, so `wm_debug` is populated. That table -- is used for geometric assertions later in the file. diff --git a/wm.sql b/wm.sql index 4c1ce44..78df7a1 100644 --- a/wm.sql +++ b/wm.sql @@ -236,12 +236,7 @@ begin -- go through each bend in the given line, and see if has a potential to -- cross bends[i]. - j = 0; - while j < array_length(bends, 1) loop - j = j + 1; - continue when i = j; - - -- do end vertices of bend[i] cross bend[j]? + for j in 1..i-1 loop a = st_pointn(bends[i], 1); b = st_pointn(bends[i], -1); multi = st_split(bends[j], st_makeline(a, b)); @@ -257,35 +252,46 @@ begin -- figure out what happens here, by hand. I know it's hard to follow. -- Apologies. prev_length = array_length(bends, 1); - if j < i then - -- remove first vertex of the following bend, because the last - -- segment is always duplicated with the i'th bend. - bends[i+1] = st_removepoint(bends[i+1], 0); - bends[j] = st_geometryn(multi, 1); - bends[j] = st_setpoint( - bends[j], - st_npoints(bends[j])-1, - st_pointn(bends[i], st_npoints(bends[i])) - ); - bends = bends[1:j] || bends[i+1:prev_length]; - j = i; - else - -- remove last vertex of the previous bend, because the last - -- segment is duplicated with the i'th bend. - bends[i-1] = st_removepoint(bends[i-1], st_npoints(bends[i-1])-1); - -- continue debugging the selfcrossing-1 here. - --raise notice 'previous bend: %', st_astext(bends[i-1]); - --raise notice 'multi: %', st_astext(multi); - --raise notice '2: removing first point from %', st_astext(st_geometryn(multi, st_numgeometries(multi))); - --mutated = false; - --return; - bends[i] = st_makeline( - st_pointn(bends[i], 1), - st_removepoint(st_geometryn(multi, st_numgeometries(multi)), 0) - ); - bends = bends[1:i] || bends[j+1:prev_length]; - end if; - j = j - prev_length + array_length(bends, 1); + + -- remove first vertex of the following bend, because the last + -- segment is always duplicated with the i'th bend. + bends[i+1] = st_removepoint(bends[i+1], 0); + bends[j] = st_geometryn(multi, 1); + bends[j] = st_setpoint( + bends[j], + st_npoints(bends[j])-1, + st_pointn(bends[i], st_npoints(bends[i])) + ); + bends = bends[1:j] || bends[i+1:prev_length]; + exit; + end loop; + + for j in reverse array_length(bends, 1)..i+1 loop + a = st_pointn(bends[i], 1); + b = st_pointn(bends[i], -1); + multi = st_split(bends[j], st_makeline(a, b)); + continue when st_numgeometries(multi) = 1; + continue when st_numgeometries(multi) = 2 and + (st_contains(bends[j], a) or st_contains(bends[j], b)); + + -- vertices, segments and stars are aligned, we are changing the bend + mutated = true; + + -- To understand the block below, I suggest you take a pencil and paper, + -- draw a self-crossing bend (fig6 from the article works well), and + -- figure out what happens here, by hand. I know it's hard to follow. + -- Apologies. + prev_length = array_length(bends, 1); + -- remove last vertex of the previous bend, because the last + -- segment is duplicated with the i'th bend. + bends[i-1] = st_removepoint(bends[i-1], st_npoints(bends[i-1])-1); + -- continue debugging the selfcrossing-1 here. + bends[i] = st_makeline( + st_pointn(bends[i], 1), + st_removepoint(st_geometryn(multi, st_numgeometries(multi)), 0) + ); + bends = bends[1:i] || bends[j+1:prev_length]; + exit; end loop; end loop; end