mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-22 08:03:07 +01:00
🏃 massive speed gain after a night of node profiling and lessons learned
This commit is contained in:
parent
461a4ab049
commit
c206055596
@ -27,14 +27,17 @@ module.exports = class BrailleBuffer
|
|||||||
termReset: "\x1B[39;49m"
|
termReset: "\x1B[39;49m"
|
||||||
|
|
||||||
constructor: (@width, @height) ->
|
constructor: (@width, @height) ->
|
||||||
@pixelBuffer = new Buffer @width*@height/8
|
size = @width*@height/8
|
||||||
|
@pixelBuffer = new Buffer size
|
||||||
|
@foregroundBuffer = new Buffer size
|
||||||
|
@backgroundBuffer = new Buffer size
|
||||||
@clear()
|
@clear()
|
||||||
|
|
||||||
clear: ->
|
clear: ->
|
||||||
@pixelBuffer.fill 0
|
@pixelBuffer.fill 0
|
||||||
@charBuffer = []
|
@charBuffer = []
|
||||||
@foregroundBuffer = []
|
@foregroundBuffer.fill 0
|
||||||
@backgroundBuffer = []
|
@backgroundBuffer.fill 0
|
||||||
|
|
||||||
setGlobalBackground: (@globalBackground) ->
|
setGlobalBackground: (@globalBackground) ->
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
bresenham = require 'bresenham'
|
bresenham = require 'bresenham'
|
||||||
earcut = require 'earcut'
|
earcut = require 'earcut'
|
||||||
|
|
||||||
BrailleBuffer = require './BrailleBuffer'
|
BrailleBuffer = require './BrailleBuffer'
|
||||||
utils = require './utils'
|
utils = require './utils'
|
||||||
|
|
||||||
@ -56,14 +55,9 @@ module.exports = class Canvas
|
|||||||
# if vertices.length
|
# if vertices.length
|
||||||
# continue
|
# continue
|
||||||
# holes.push vertices.length/2
|
# holes.push vertices.length/2
|
||||||
|
polylines.push polylines[0]
|
||||||
for point in polylines
|
for point in polylines
|
||||||
vertices = vertices.concat point
|
vertices = vertices.concat point
|
||||||
xs[point[0]] = ys[point[1]] = true
|
|
||||||
|
|
||||||
# Check if we actually got a valid polygon after projection and clamping
|
|
||||||
if Object.keys(xs).length is 1 or Object.keys(ys).length is 1
|
|
||||||
return false
|
|
||||||
|
|
||||||
try
|
try
|
||||||
triangles = earcut vertices, holes
|
triangles = earcut vertices, holes
|
||||||
@ -78,9 +72,6 @@ module.exports = class Canvas
|
|||||||
pb = extract(triangles[i+1])
|
pb = extract(triangles[i+1])
|
||||||
pc = extract(triangles[i+2])
|
pc = extract(triangles[i+2])
|
||||||
|
|
||||||
if (0 <= pa[0] < @width and 0 <= pa[1] < @height) or
|
|
||||||
(0 <= pb[0] < @width and 0 <= pb[1] < @height) or
|
|
||||||
(0 <= pc[0] < @width and 0 <= pc[1] < @height)
|
|
||||||
@_filledTriangle pa, pb, pc, color
|
@_filledTriangle pa, pb, pc, color
|
||||||
|
|
||||||
# Inspired by Alois Zingl's "The Beauty of Bresenham's Algorithm"
|
# Inspired by Alois Zingl's "The Beauty of Bresenham's Algorithm"
|
||||||
@ -159,13 +150,17 @@ module.exports = class Canvas
|
|||||||
[lastPoint, last] = [last, point]
|
[lastPoint, last] = [last, point]
|
||||||
not lastPoint or lastPoint.x isnt point.x or lastPoint.y isnt point.y
|
not lastPoint or lastPoint.x isnt point.x or lastPoint.y isnt point.y
|
||||||
|
|
||||||
for i, point of points
|
for i in [0...points.length]
|
||||||
|
point = points[i]
|
||||||
next = points[i*1+1]
|
next = points[i*1+1]
|
||||||
|
|
||||||
if point.y is next?.y
|
if point.y is next?.y
|
||||||
left = Math.max 0, point.x
|
left = Math.max 0, point.x
|
||||||
right = Math.min @width, next?.x
|
right = Math.min @width, next.x
|
||||||
|
if left and right
|
||||||
@buffer.setPixel x, point.y, color for x in [left..right]
|
@buffer.setPixel x, point.y, color for x in [left..right]
|
||||||
|
|
||||||
else
|
else
|
||||||
@buffer.setPixel point.x, point.y, color
|
@buffer.setPixel point.x, point.y, color
|
||||||
|
|
||||||
|
break unless next
|
||||||
|
@ -210,18 +210,19 @@ module.exports = class Renderer
|
|||||||
if feature.style.minzoom and tile.zoom < feature.style.minzoom
|
if feature.style.minzoom and tile.zoom < feature.style.minzoom
|
||||||
return false
|
return false
|
||||||
|
|
||||||
switch feature.style.type
|
|
||||||
when "line"
|
|
||||||
points = @_scaleAndReduce tile, feature, feature.points
|
points = @_scaleAndReduce tile, feature, feature.points
|
||||||
|
|
||||||
|
switch feature.style.type
|
||||||
|
when "line"
|
||||||
width = feature.style.paint['line-width']
|
width = feature.style.paint['line-width']
|
||||||
width = width.stops[0][1] if width instanceof Object
|
width = width.stops[0][1] if width instanceof Object
|
||||||
|
|
||||||
@canvas.polyline points, feature.color, width if points.length
|
@canvas.polyline points, feature.color, width if points.length
|
||||||
|
|
||||||
when "fill"
|
when "fill"
|
||||||
vertices = (@_scaleAndReduce tile, feature, points, false for points in feature.points)
|
@canvas.polygon points, feature.color
|
||||||
@canvas.polygon vertices[0], feature.color
|
# if points.length is 3
|
||||||
|
# @canvas._filledTriangle points[0], points[1], points[2], feature.color
|
||||||
true
|
true
|
||||||
|
|
||||||
when "symbol"
|
when "symbol"
|
||||||
@ -242,12 +243,15 @@ module.exports = class Renderer
|
|||||||
else if @config.layers[feature.layer]?.cluster and @labelBuffer.writeIfPossible "X", point[0], point[1], feature, 3
|
else if @config.layers[feature.layer]?.cluster and @labelBuffer.writeIfPossible "X", point[0], point[1], feature, 3
|
||||||
@canvas.text "◉", point[0], point[1], feature.color
|
@canvas.text "◉", point[0], point[1], feature.color
|
||||||
|
|
||||||
|
true
|
||||||
|
|
||||||
_seen: {}
|
_seen: {}
|
||||||
_scaleAndReduce: (tile, feature, points, filter = true) ->
|
_scaleAndReduce: (tile, feature, points, filter = true) ->
|
||||||
lastX = null
|
lastX = null
|
||||||
lastY = null
|
lastY = null
|
||||||
outside = false
|
outside = false
|
||||||
scaled = []
|
scaled = []
|
||||||
|
#seen = {}
|
||||||
|
|
||||||
for point in points
|
for point in points
|
||||||
x = Math.floor tile.position.x+(point.x/tile.scale)
|
x = Math.floor tile.position.x+(point.x/tile.scale)
|
||||||
@ -259,8 +263,12 @@ module.exports = class Renderer
|
|||||||
lastY = y
|
lastY = y
|
||||||
lastX = x
|
lastX = x
|
||||||
|
|
||||||
|
# TODO: benchmark
|
||||||
|
# continue if seen[idx = (y<<8)+x]
|
||||||
|
# seen[idx] = true
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
if tile.xyz.z > 1 and (
|
if (
|
||||||
x < -@tilePadding or
|
x < -@tilePadding or
|
||||||
y < -@tilePadding or
|
y < -@tilePadding or
|
||||||
x > @width+@tilePadding or
|
x > @width+@tilePadding or
|
||||||
@ -277,8 +285,8 @@ module.exports = class Renderer
|
|||||||
|
|
||||||
if filter
|
if filter
|
||||||
if scaled.length is 2
|
if scaled.length is 2
|
||||||
if @_seen[ka = scaled[0].concat(scaled[1]).join '-'] or
|
if @_seen[ka = (scaled[0]<<8)+scaled[1]] or
|
||||||
@_seen[kb = scaled[1].concat(scaled[0]).join '-']
|
@_seen[kb = (scaled[1]<<8)+scaled[0]]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@_seen[ka] = @_seen[kb] = true
|
@_seen[ka] = @_seen[kb] = true
|
||||||
|
@ -23,8 +23,8 @@ module.exports = class Termap
|
|||||||
styleFile: __dirname+"/../styles/bright.json"
|
styleFile: __dirname+"/../styles/bright.json"
|
||||||
|
|
||||||
initialZoom: null
|
initialZoom: null
|
||||||
maxZoom: 17
|
maxZoom: 18
|
||||||
zoomStep: 0.1
|
zoomStep: 0.2
|
||||||
headless: false
|
headless: false
|
||||||
|
|
||||||
# size:
|
# size:
|
||||||
@ -154,7 +154,9 @@ module.exports = class Termap
|
|||||||
when "q"
|
when "q"
|
||||||
process.exit 0
|
process.exit 0
|
||||||
|
|
||||||
when "w" then @zoomy = true
|
when "w" then @zoomy = 1
|
||||||
|
when "s" then @zoomy = -1
|
||||||
|
|
||||||
when "a" then @zoomBy @config.zoomStep
|
when "a" then @zoomBy @config.zoomStep
|
||||||
when "z" then @zoomBy -@config.zoomStep
|
when "z" then @zoomBy -@config.zoomStep
|
||||||
|
|
||||||
@ -181,11 +183,12 @@ module.exports = class Termap
|
|||||||
.catch =>
|
.catch =>
|
||||||
@notify "renderer is busy"
|
@notify "renderer is busy"
|
||||||
.then =>
|
.then =>
|
||||||
if @zoomy and @zoom < @config.maxZoom
|
if @zoomy
|
||||||
@zoom += @config.zoomStep
|
if (@zoomy > 0 and @zoom < @config.maxZoom) or (@zoomy < 0 and @zoom > @minZoom)
|
||||||
@_draw()
|
@zoom += @zoomy * @config.zoomStep
|
||||||
else
|
else
|
||||||
@zoomy = false
|
@zoomy *= -1
|
||||||
|
@_draw()
|
||||||
|
|
||||||
_getFooter: ->
|
_getFooter: ->
|
||||||
# features = @renderer.featuresAt @mousePosition.x-1-(@view[0]>>1), @mousePosition.y-1-(@view[1]>>2)
|
# features = @renderer.featuresAt @mousePosition.x-1-(@view[0]>>1), @mousePosition.y-1-(@view[1]>>2)
|
||||||
@ -195,16 +198,11 @@ module.exports = class Termap
|
|||||||
# type: f.feature.properties.type
|
# type: f.feature.properties.type
|
||||||
# rank: f.feature.properties.scalerank
|
# rank: f.feature.properties.scalerank
|
||||||
# ).join(", ")+"] "+
|
# ).join(", ")+"] "+
|
||||||
"#{@mousePosition.x} #{@mousePosition.y} " +
|
#{}"#{@mousePosition.x} #{@mousePosition.y} " +
|
||||||
#"center: [#{utils.digits @center.lat, 2}, #{utils.digits @center.lng, 2}]}"
|
|
||||||
# bbox = @_getBBox()
|
# bbox = @_getBBox()
|
||||||
# tiles = @_tilesInBBox(bbox)
|
# tiles = @_tilesInBBox(bbox)
|
||||||
|
"center: #{utils.digits @center.lat, 3}, #{utils.digits @center.lon, 3} "+
|
||||||
"zoom: #{utils.digits @zoom, 2} "
|
"zoom: #{utils.digits @zoom, 2} "
|
||||||
#{}"bbox: [#{bbox.map((z) -> utils.digits(z, 2)).join(', ')}]"+
|
|
||||||
# "tiles: "+("#{k}: #{v}" for k,v of @_tilesInBBox(bbox) when typeof v is "number").join(",")
|
|
||||||
|
|
||||||
|
|
||||||
#features.map((f) -> JSON.stringify f.feature.properties).join(" - ")
|
|
||||||
|
|
||||||
notify: (text) ->
|
notify: (text) ->
|
||||||
@_write "\r\x1B[K"+text unless @config.headless
|
@_write "\r\x1B[K"+text unless @config.headless
|
||||||
|
@ -11,6 +11,7 @@ Promise = require 'bluebird'
|
|||||||
zlib = require 'zlib'
|
zlib = require 'zlib'
|
||||||
rbush = require 'rbush'
|
rbush = require 'rbush'
|
||||||
x256 = require 'x256'
|
x256 = require 'x256'
|
||||||
|
earcut = require 'earcut'
|
||||||
|
|
||||||
utils = require "./utils"
|
utils = require "./utils"
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ class Tile
|
|||||||
|
|
||||||
for name, layer of tile.layers
|
for name, layer of tile.layers
|
||||||
nodes = []
|
nodes = []
|
||||||
|
#continue if name is "water"
|
||||||
for i in [0...layer.length]
|
for i in [0...layer.length]
|
||||||
# TODO: caching of similar attributes to avoid looking up the style each time
|
# TODO: caching of similar attributes to avoid looking up the style each time
|
||||||
#continue if @styler and not @styler.getStyleFor layer, feature
|
#continue if @styler and not @styler.getStyleFor layer, feature
|
||||||
@ -62,7 +64,7 @@ class Tile
|
|||||||
style.paint['fill-color'] or
|
style.paint['fill-color'] or
|
||||||
style.paint['text-color']
|
style.paint['text-color']
|
||||||
|
|
||||||
# TODO: zoom calculation todo for perfect styling
|
# TODO: style zoom stops handling
|
||||||
if color instanceof Object
|
if color instanceof Object
|
||||||
color = color.stops[0][1]
|
color = color.stops[0][1]
|
||||||
|
|
||||||
@ -72,18 +74,7 @@ class Tile
|
|||||||
# use feature.loadGeometry() again as soon as we got a 512 extent tileset
|
# use feature.loadGeometry() again as soon as we got a 512 extent tileset
|
||||||
geometries = feature.loadGeometry() #@_reduceGeometry feature, 8
|
geometries = feature.loadGeometry() #@_reduceGeometry feature, 8
|
||||||
|
|
||||||
# TODO: handling polygon holes, only handling outer area for now
|
for points in (if style.type is "fill" then [geometries[0]] else geometries)
|
||||||
if style.type is "fill"
|
|
||||||
nodes.push @_addBoundaries
|
|
||||||
id: feature.id
|
|
||||||
layer: name
|
|
||||||
style: style
|
|
||||||
properties: feature.properties
|
|
||||||
points: geometries
|
|
||||||
color: colorCode
|
|
||||||
|
|
||||||
else
|
|
||||||
for points in geometries
|
|
||||||
nodes.push @_addBoundaries
|
nodes.push @_addBoundaries
|
||||||
id: feature.id
|
id: feature.id
|
||||||
layer: name
|
layer: name
|
||||||
@ -92,26 +83,26 @@ class Tile
|
|||||||
points: points
|
points: points
|
||||||
color: colorCode
|
color: colorCode
|
||||||
|
|
||||||
tree = rbush()
|
|
||||||
|
tree = rbush 18
|
||||||
tree.load nodes
|
tree.load nodes
|
||||||
|
|
||||||
layers[name] = tree
|
layers[name] = tree
|
||||||
|
|
||||||
@layers = layers
|
@layers = layers
|
||||||
|
|
||||||
_addBoundaries: (data) ->
|
_addBoundaries: (data) ->
|
||||||
[minX, maxX, minY, maxY] = [Infinity, -Infinity, Infinity, -Infinity]
|
minX = Infinity
|
||||||
minMax = (points) ->
|
maxX = -Infinity
|
||||||
for p in points
|
minY = Infinity
|
||||||
|
maxY = -Infinity
|
||||||
|
|
||||||
|
for p in data.points
|
||||||
minX = p.x if p.x < minX
|
minX = p.x if p.x < minX
|
||||||
maxX = p.x if p.x > maxX
|
maxX = p.x if p.x > maxX
|
||||||
minY = p.y if p.y < minY
|
minY = p.y if p.y < minY
|
||||||
maxY = p.y if p.y > maxY
|
maxY = p.y if p.y > maxY
|
||||||
|
|
||||||
if data.points[0] instanceof Array
|
|
||||||
minMax points for points in data.points
|
|
||||||
else
|
|
||||||
minMax data.points
|
|
||||||
|
|
||||||
data.minX = minX
|
data.minX = minX
|
||||||
data.maxX = maxX
|
data.maxX = maxX
|
||||||
data.minY = minY
|
data.minY = minY
|
||||||
|
Loading…
Reference in New Issue
Block a user