Skip to content
Permalink
Browse files

WorldMap line rendering: clip long lines protruding out of viewport

fixes #205
  • Loading branch information...
vpithart authored and LarsMichelsen committed Jul 10, 2019
1 parent 75d310e commit 8455e02f78dc7effd8e3d68bc4d670799db6dc04
Showing with 150 additions and 1 deletion.
  1. +1 −0 ChangeLog
  2. +149 −1 share/frontend/nagvis-js/js/ElementLine.js
@@ -5,6 +5,7 @@ Core:

Worldmap:
* Consistent page reloads: center + zoom of the map kept in URL
* Memory and performance optimization: line rendering - clip long lines protruding out of viewport

Frontend
* FIX: Fix creating users when mbstring extension is not available
@@ -340,6 +340,12 @@ var ElementLine = Element.extend({
var xEnd = x[x.length - 1];
var yEnd = y[y.length - 1];

let adjustedPoints = this.cutLineBeyondViewport(xStart, yStart, xEnd, yEnd);
if (adjustedPoints === null) {
return; // Line won't be visible -> this.parts left empty -> no rendering
}
[xStart, yStart, xEnd, yEnd] = adjustedPoints;

var width = addZoomFactor(this.obj.conf.line_width);
if (width <= 0)
width = 1; // minimal width for lines
@@ -422,6 +428,10 @@ var ElementLine = Element.extend({
},

renderLine: function() {
if (this.parts.length === 0) {
return;
}

var allX = [], allY = [];
for (var i = 0, len = this.parts.length; i < len; i++) {
allX = allX.concat(this.parts[i][2]);
@@ -948,6 +958,144 @@ var ElementLine = Element.extend({
]);
}
return parsed;
}
},

/*
* There is a rectangle: viewport (current Leaflet map shown) + 10% on each side.
* This function cuts the lines protruding beyond the rectangle.
* Shortened lines prevent renderLine() from generating many vast <canvas> (10000-ish pixels) which eventually lead
* to memory exhaustion or crash the browser
*
* Returns:
* either: [xA, yA, xB, yB] - original or adjusted line A/B coordinates,
* or: null - line out of a rectangle, don't render
*/

cutLineBeyondViewport: function(xA, yA, xB, yB) {
let viewport_size = g_map.getSize();
const xMax = viewport_size.x; // 1920
const yMax = viewport_size.y; // 1080
const xTolerance = xMax * 0.1; // 192 (10%)
const yTolerance = yMax * 0.1; // 108 (10%)

// Cohen-Sutherland algorithm

// region codes
const INSIDE = 0; // 0000
const LEFT = 1; // 0001
const RIGHT = 2; // 0010
const BOTTOM = 4; // 0100
const TOP = 8; // 1000

// boundaries
const x_min = -xTolerance;
const x_max = xMax + xTolerance;
const y_min = -yTolerance;
const y_max = yMax + yTolerance;

// Function to compute region code
let regionCode = function (x, y)
{
let code = INSIDE;

if (x < x_min) // to the left of rectangle
code |= LEFT;
else if (x > x_max) // to the right of rectangle
code |= RIGHT;
if (y < y_min) // below the rectangle
code |= BOTTOM;
else if (y > y_max) // above the rectangle
code |= TOP;

return code;
}

let cohenSutherlandClip = function (x1, y1, x2, y2) {
let code1 = regionCode(x1, y1);
let code2 = regionCode(x2, y2);

let accept = false;

while (true)
{
if ((code1 == 0) && (code2 == 0))
{
// If both endpoints lie within rectangle
accept = true;
break;
}
else if (code1 & code2)
{
// If both endpoints are outside rectangle,
// in same region
break;
}
else
{
// Some segment of line lies within the
// rectangle
let code_out;
let x, y;

// At least one endpoint is outside the
// rectangle, pick it.
if (code1 != 0)
code_out = code1;
else
code_out = code2;

// Find intersection point;
// using formulas y = y1 + slope * (x - x1),
// x = x1 + (1 / slope) * (y - y1)
if (code_out & TOP)
{
// point is above the clip rectangle
x = x1 + (x2 - x1) * (y_max - y1) / (y2 - y1);
y = y_max;
}
else if (code_out & BOTTOM)
{
// point is below the rectangle
x = x1 + (x2 - x1) * (y_min - y1) / (y2 - y1);
y = y_min;
}
else if (code_out & RIGHT)
{
// point is to the right of rectangle
y = y1 + (y2 - y1) * (x_max - x1) / (x2 - x1);
x = x_max;
}
else if (code_out & LEFT)
{
// point is to the left of rectangle
y = y1 + (y2 - y1) * (x_min - x1) / (x2 - x1);
x = x_min;
}

// Now intersection point x,y is found
// We replace point outside rectangle
// by intersection point
if (code_out == code1)
{
x1 = x;
y1 = y;
code1 = regionCode(x1, y1);
}
else
{
x2 = x;
y2 = y;
code2 = regionCode(x2, y2);
}
}
}
if (accept)
return([x1, y1, x2, y2]);
else
return null;
}

return cohenSutherlandClip(xA, yA, xB, yB);
},

});

0 comments on commit 8455e02

Please sign in to comment.
You can’t perform that action at this time.