]> mj.ucw.cz Git - leo.git/blobdiff - simplify.c
Simplify: Splitting of ways to non-branching segments
[leo.git] / simplify.c
index a621027c70f143df0d5494a99a1f67167214302d..f5dc717edad92b0c4eecacd3e820428da2e62021 100644 (file)
@@ -4,6 +4,9 @@
  *     (c) 2022 Martin Mares <mj@ucw.cz>
  */
 
+// #define LOCAL_DEBUG
+// #pragma GCC optimize("-O0")
+
 #include "leo.h"
 
 #include <ucw/lib.h>
 
 #include "osm.h"
 #include "map.h"
+#include "sym.h"
 #include "simplify.h"
 
 static struct mempool *simp_pool;
+static struct osm *simp_osm;
+static osm_id_t simp_last_id;
 
 struct coords {
   double x, y;
 };
 
-struct simp_node_ref {
-  cnode n;
-  struct osm_node *to;
-};
-
 struct simp_way_ref {
   cnode n;
-  struct osm_way *to;
-  bool is_end;
+  struct simp_node *snode;
+  struct symbol *sym;
+  z_index_t zindex;
+  struct osm_way *way;
+  struct osm_node *node;
+  struct simp_way_ref *prev_on_way;
+  struct simp_way_ref *next_on_way;
+  byte merge_done;
 };
 
 struct simp_node {
@@ -39,16 +46,12 @@ struct simp_node {
     struct coords c;
     byte raw_coords[sizeof(struct coords)];
   };
-  clist node_refs;
   clist way_refs;
-  byte can_be_merged;
 };
 
 static void simp_hash_init_data(struct simp_node *s)
 {
-  clist_init(&s->node_refs);
   clist_init(&s->way_refs);
-  s->can_be_merged = false;
 }
 
 #define HASH_NODE struct simp_node
@@ -61,64 +64,327 @@ static void simp_hash_init_data(struct simp_node *s)
 #define HASH_TABLE_ALLOC
 #include <ucw/hashtable.h>
 
-static bool simp_can_be_merged(struct simp_node *s)
+static struct simp_node *simp_lookup_node(struct osm_node *n)
+{
+  struct coords c = { .x = n->x, .y = n->y };
+  return simp_hash_lookup((byte *) &c);
+}
+
+static void simp_register_symbol(struct symbol *sym, z_index_t zindex)
 {
-  uint num_nodes = clist_size(&s->node_refs);
-  uint num_ways = clist_size(&s->way_refs);
+  struct osm_object *o = sym->o;
 
-  ASSERT(num_nodes);
-  if (num_nodes > 1)
+  switch (o->type)
     {
-      msg(L_WARN, "Simplify: Multiple (%u) nodes at [%f, %f]", num_nodes, s->c.x, s->c.y);
-      return false;
+    case OSM_TYPE_NODE:
+      simp_lookup_node((struct osm_node *) o);
+      break;
+    case OSM_TYPE_WAY:
+      {
+       struct osm_way *w = (struct osm_way *) o;
+       struct simp_way_ref *wr_last = NULL;
+       struct osm_node *prev_n = NULL;
+
+#if 0
+       for (uint i=0; i<2; i++)
+         {
+           struct osm_node *n = (struct osm_node *) (i ? osm_ref_head(&w->nodes) : osm_ref_tail(&w->nodes));
+           struct sym_point *sp = sym_point_new(&n->o);
+           sp->shape = VALUE_CIRCLE;
+           sp->size = 1;
+           sp->fill_color = 0x0000ff;
+           sp->fill_opacity = 1;
+           sp->do_fill = 1;
+           sym_plan(sp, 100);
+         }
+#endif
+
+       if (clist_size(&w->nodes) < 2)
+         break;
+
+       OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
+         {
+           if (n == prev_n)
+             continue;
+
+           struct simp_node *s = simp_lookup_node(n);
+           struct simp_way_ref *wref = mp_alloc(simp_pool, sizeof(*wref));
+           wref->snode = s;
+           wref->sym = sym;
+           wref->zindex = zindex;
+           wref->node = n;
+           wref->way = w;
+           wref->prev_on_way = wr_last;
+           wref->next_on_way = NULL;
+           if (wr_last)
+             wr_last->next_on_way = wref;
+           wr_last = wref;
+           wref->merge_done = 0;
+           clist_add_tail(&s->way_refs, &wref->n);
+
+           prev_n = n;
+         }
+       OSM_FOR_EACH_END;
+       break;
+      }
+    default:
+      die("Simplify: Unsupported symbol type %u", o->type);
     }
+}
 
-  if (num_ways > 2)
-    return false;
+static struct simp_way_ref *wref_other_end(struct simp_way_ref *wr)
+{
+  if (wr->prev_on_way)
+    {
+      while (wr->prev_on_way)
+       wr = wr->prev_on_way;
+    }
+  else
+    {
+      while (wr->next_on_way)
+       wr = wr->next_on_way;
+    }
+  return wr;
+}
+
+static struct simp_way_ref *wref_only_other(struct simp_node *sn, struct simp_way_ref *in_wr)
+{
+  struct simp_way_ref *out_wr = NULL;
+
+  CLIST_FOR_EACH(struct simp_way_ref *, wr, sn->way_refs)
+    {
+      if (wr == in_wr)
+       ;
+      else if (!out_wr)
+       out_wr = wr;
+      else
+       return NULL;
+    }
 
-  return false;
+  return out_wr;
 }
 
-static void simplify_source(struct data_source *ds)
+/*** Merging of ways ***/
+
+static struct simp_way_ref **merge_wrefs;
+static uint num_merged_ways, num_merged_segments;
+
+static void simp_merge_way(struct simp_node *s)
 {
-  msg(L_INFO, "Simplifying source %s", ds->file);
-  simp_pool = mp_new(65536);
-  simp_hash_init();
-  osm_this = ds->osm;
+  // XXX: This is grossly over-simplified. We assume that every way generates
+  // at most one symbol, which is definitely not the case for ways rendered
+  // with casing. But it should be sufficient for now.
+
+  uint nrefs = clist_size(&s->way_refs);
+  if (nrefs != 2)
+    return;
+
+  struct simp_way_ref *wr1, *wr2, *wr;
+  wr1 = clist_head(&s->way_refs);
+  wr2 = clist_tail(&s->way_refs);
+  if (!(!wr1->prev_on_way != !wr1->next_on_way && !wr2->prev_on_way != !wr2->next_on_way))
+    return;
+  if (wr1->merge_done || wr2->merge_done)
+    return;
+
+  wr = wr1;
+  DBG("Merge: Starting with wr %p", wr);
+
+  for (;;)
+    {
+      wr->merge_done = 1;
+      struct simp_way_ref *or = wref_other_end(wr);
+      struct simp_node *on = or->snode;
+
+      struct simp_way_ref *cr = wref_only_other(on, or);
+      DBG("XXX: wr=%p or=%p cr=%p", wr, or, cr);
+      if (!cr || !sym_same_attrs_p(or->sym, cr->sym))
+       {
+         DBG("Merge: Back stop at %p", cr);
+         break;
+       }
+
+      DBG("Merge: Back to %p", cr);
+      wr = cr;
+
+      if (wr == wr1)
+       {
+         DBG("Merge: Loop detected");
+         return;
+       }
+    }
+
+  // Now, we are at the beginning/end of a single way
+
+  DBG("Merge: Back to %p", wr);
+  wr = wref_other_end(wr);
+  DBG("Merge: Other end is %p", wr);
+  GARY_RESIZE(merge_wrefs, 0);
+  *GARY_PUSH(merge_wrefs) = wr;
+
+  for (;;)
+    {
+      wr->merge_done = 1;
+      struct simp_way_ref *or = wref_other_end(wr);
+      struct simp_node *on = or->snode;
+
+      struct simp_way_ref *cr = wref_only_other(on, or);
+      if (!cr || !sym_same_attrs_p(or->sym, cr->sym))
+       {
+         DBG("Merge: Fwd stop at %p", cr);
+         break;
+       }
+
+      DBG("Merge: Fwd to %p", cr);
+      *GARY_PUSH(merge_wrefs) = cr;
+      wr = cr;
+    }
+
+  if (GARY_SIZE(merge_wrefs) < 2)
+    return;
+
+  DBG("Will merge");
+  num_merged_ways++;
 
-  CLIST_FOR_EACH(struct osm_way *, w, osm_this->obj_list[OSM_TYPE_WAY])
+  struct osm_way *nw = osm_way_new(++simp_last_id);
+  struct symbol *main_sym = NULL;
+
+  for (uint i=0; i < GARY_SIZE(merge_wrefs); i++)
     {
-      struct osm_node *n_first = (struct osm_node *) osm_ref_head(&w->nodes);
-      struct osm_node *n_last = (struct osm_node *) osm_ref_tail(&w->nodes);
+      wr = merge_wrefs[i];
+      clist_remove(&wr->n);
+      if (!i)
+       {
+         osm_way_add_node(nw, wr->node);
+         main_sym = wr->sym;
+         main_sym->o = &nw->o;
+       }
+      else
+       sym_disable(wr->sym);
 
-      OSM_FOR_EACH_BEGIN(struct osm_node *, n, w->nodes)
+      if (wr->prev_on_way)
        {
-         struct coords c = { .x = n->x, .y = n->y };
-         struct simp_node *s = simp_hash_lookup((byte *) &c);
+         while (wr = wr->prev_on_way)
+           {
+             clist_remove(&wr->n);
+             osm_way_add_node(nw, wr->node);
+           }
+       }
+      else
+       {
+         // ASSERT(wr->next_on_way);
+         while (wr = wr->next_on_way)
+           {
+             clist_remove(&wr->n);
+             osm_way_add_node(nw, wr->node);
+           }
+       }
+
+      num_merged_segments++;
+    }
+
+  // osm_obj_dump(&nw->o);
+  simp_register_symbol(main_sym, wr1->zindex);
+  // ((struct sym_line *) main_sym)->color = 0x0000ff;
+}
+
+static void simp_merge_ways(void)
+{
+  HASH_FOR_ALL(simp_hash, s)
+    {
+      simp_merge_way(s);
+    }
+  HASH_END_FOR;
+
+  msg(L_INFO, "Simplify: Merged %u segments to %u new ways", num_merged_segments, num_merged_ways);
+}
+
+/*** Splitting of ways ***/
+
+static struct simp_way_ref **split_breakpoints;
+static uint num_split_ways, num_split_segments;
+
+static void simp_split_way(struct simp_way_ref *wr)
+{
+  GARY_RESIZE(split_breakpoints, 0);
+  for (struct simp_way_ref *tr=wr; tr; tr=tr->next_on_way)
+    if (tr->prev_on_way && tr->next_on_way)
+      {
+       struct simp_node *sn = tr->snode;
+       struct simp_way_ref *x = clist_head(&sn->way_refs);
+       if (x && clist_next(&sn->way_refs, &x->n))
+         *GARY_PUSH(split_breakpoints) = tr;
+      }
+
+  uint num_bp = GARY_SIZE(split_breakpoints);
+  if (!num_bp)
+    return;
 
-         struct simp_node_ref *nref = mp_alloc(simp_pool, sizeof(*nref));
-         nref->to = n;
-         clist_add_tail(&s->node_refs, &nref->n);
+  DBG("Split: Want to split wr %p (%u times)", wr, num_bp);
+  // ((struct sym_line *) wr->sym)->color = 0x0000ff;
+  num_split_ways++;
 
-         struct simp_way_ref *wref = mp_alloc(simp_pool, sizeof(*wref));
-         wref->to = w;
-         wref->is_end = (n == n_first || n == n_last);
-         clist_add_tail(&s->way_refs, &wref->n);
+  uint bp = 0;
+  struct symbol *orig_sym = wr->sym;
+  struct symbol *sym = NULL;
+  struct osm_way *w = NULL;
+
+  for (struct simp_way_ref *tr=wr; tr; tr=tr->next_on_way)
+    {
+      clist_remove(&tr->n);
+      if (!sym)
+       {
+         w = osm_way_new(++simp_last_id);
+         sym = sym_clone(orig_sym);
+         sym->o = &w->o;
+       }
+      osm_way_add_node(w, tr->node);
+      if (bp < num_bp && split_breakpoints[bp] == tr)
+       {
+         sym_plan(sym, wr->zindex);
+         num_split_segments++;
+         sym = NULL;
+         w = NULL;
+         bp++;
        }
-      OSM_FOR_EACH_END;
     }
 
+  if (sym)
+    {
+      num_split_segments++;
+      sym_plan(sym, wr->zindex);
+      simp_register_symbol(sym, wr->zindex);
+    }
+
+  sym_disable(orig_sym);
+}
+
+static void simp_split_ways(void)
+{
   HASH_FOR_ALL(simp_hash, s)
     {
-      s->can_be_merged = simp_can_be_merged(s);
+      struct simp_way_ref *wr, *tmp;
+      CLIST_WALK_DELSAFE(wr, s->way_refs, tmp)
+       if (!wr->prev_on_way && wr->next_on_way)
+         simp_split_way(wr);
     }
   HASH_END_FOR;
 
-  mp_delete(simp_pool);
+  msg(L_INFO, "Simplify: Split %u ways to %u segments", num_split_ways, num_split_segments);
 }
 
 void simplify(void)
 {
-  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
-    simplify_source(ds);
+  msg(L_INFO, "Simplifying symbol topology");
+  simp_pool = mp_new(65536);
+  simp_osm = osm_init();
+  GARY_INIT_ALLOC(merge_wrefs, 0, &simp_pool->allocator);
+  GARY_INIT_ALLOC(split_breakpoints, 0, &simp_pool->allocator);
+  simp_hash_init();
+
+  sym_for_all_planned(simp_register_symbol);
+  simp_split_ways();
+  simp_merge_ways();
+
+  mp_delete(simp_pool);
 }