login

<     >

2021-08-23 09:17:27 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

implement lazy iterator API

Simplest way to create an iterator in Lua is defining a closure
that returns either an item or nil, where nil is the sentinel.

This change makes Surf:poly*() accept either tables or iterators.
Also, Frame:mapped() both accepts and returns iterators.

Lazily drawing a polygon from a shapefile now looks like this:
    geobbox, lens, geopgons = sf:read_polygons(index)
    surf:polygons(frame:mapped(geopgons), 1)

diff --git a/map.lua b/map.lua
index 0e42d13..8ff1c38 100644
--- a/map.lua
+++ b/map.lua
@@ -181,14 +181,20 @@ function Frame:set_height(h)
     self.w = math.floor(mw * self.s + 0.5)
 end
 
-function Frame:mapped(points)
-    return coroutine.wrap(function()
-        for p in points do
-            local lat, lon = unpack(p)
-            local x, y = self:map(lat, lon)
-            coroutine.yield({x, y})
+function Frame:mapped(polys)
+    return function()
+        local points = polys()
+        if points then
+            return function()
+                local point = points()
+                if point then
+                    local lat, lon = unpack(point)
+                    local x, y = self:map(lat, lon)
+                    return {x, y}
+                end
+            end
         end
-    end)
+    end
 end
 
 function Frame:save(fname)

diff --git a/surf.lua b/surf.lua
index 7f1f9af..a0c8282 100644
--- a/surf.lua
+++ b/surf.lua
@@ -17,6 +17,20 @@ local function round(x)
     return i
 end
 
+local function func_iter(seq)
+    if type(seq) == "table" then
+        local i = 0
+        return function()
+            i = i + 1
+            if i <= #seq then
+                return seq[i]
+            end
+        end
+    else
+        return seq
+    end
+end
+
 local Surf = {}
 
 function Surf:inside(x, y)
@@ -71,18 +85,20 @@ function Surf:line(x0, y0, x1, y1, v, r)
 end
 
 function Surf:polyline(points, v, r)
+    points = func_iter(points)
     local x0, y0, x1, y1
-    x0, y0 = unpack(points[1])
-    for i = 2, #points do
-        x1, y1 = unpack(points[i])
+    x0, y0 = unpack(points())
+    for point in points do
+        x1, y1 = unpack(point)
         self:line(x0, y0, x1, y1, v, r)
         x0, y0 = x1, y1
     end
 end
 
-function Surf:polylines(polys, v, r)
-    for _, lines in ipairs(polys) do
-        self:polyline(lines, v, r)
+function Surf:polylines(polylines, v, r)
+    polylines = func_iter(polylines)
+    for points in polylines do
+        self:polyline(points, v, r)
     end
 end
 
@@ -91,6 +107,7 @@ local function cross_comp(a, b)
 end
 
 function Surf:scan(points)
+    points = func_iter(points)
     if not self.scans then
         self.scans = {}
         for i = 0, self.h-1 do
@@ -100,9 +117,9 @@ function Surf:scan(points)
     local x0, y0, x1, y1
     local ax, ay, bx, by -- same line as above, but enforce ay < by
     local sign
-    x0, y0 = unpack(points[1])
-    for i = 2, #points do
-        x1, y1 = unpack(points[i])
+    x0, y0 = unpack(points())
+    for point in points do
+        x1, y1 = unpack(point)
         if y1 ~= y0 then
             if y0 < y1 then
                 ax, ay, bx, by = x0, y0, x1, y1
@@ -154,7 +171,8 @@ function Surf:polygon(points, v)
 end
 
 function Surf:polygons(polygons, v)
-    for _, points in ipairs(polygons) do
+    polygons = func_iter(polygons)
+    for points in polygons do
         self:scan(points)
     end
     self:fill_scans(v)