ol
Version:
OpenLayers mapping library
155 lines (147 loc) • 4.15 kB
JavaScript
/**
* @module ol/geom/flat/clip
*/
/**
* Start parameter of the portion of the last clipped segment that is inside the
* extent. Set by {@link clipSegment}, read right after a `true` return to avoid
* allocating a result array per segment.
* @type {number}
*/
let clipSegmentStart = 0;
/**
* End parameter of the portion of the last clipped segment that is inside the
* extent. See {@link clipSegmentStart}.
* @type {number}
*/
let clipSegmentEnd = 1;
/**
* Clip a segment to a rectangular extent. On a `true` return, the inside
* portion is `[clipSegmentStart, clipSegmentEnd]` in segment parameters. No
* allocations are made, so the result globals must be read before the next
* call.
* @param {number} minX Minimum X.
* @param {number} minY Minimum Y.
* @param {number} maxX Maximum X.
* @param {number} maxY Maximum Y.
* @param {number} x0 Segment start X.
* @param {number} y0 Segment start Y.
* @param {number} x1 Segment end X.
* @param {number} y1 Segment end Y.
* @return {boolean} The segment intersects the extent.
*/
function clipSegment(minX, minY, maxX, maxY, x0, y0, x1, y1) {
const dx = x1 - x0;
const dy = y1 - y0;
let t0 = 0;
let t1 = 1;
// X slab.
if (dx === 0) {
if (x0 < minX || x0 > maxX) {
return false;
}
} else {
let ta = (minX - x0) / dx;
let tb = (maxX - x0) / dx;
if (ta > tb) {
const tmp = ta;
ta = tb;
tb = tmp;
}
if (ta > t0) {
t0 = ta;
}
if (tb < t1) {
t1 = tb;
}
if (t0 > t1) {
return false;
}
}
// Y slab.
if (dy === 0) {
if (y0 < minY || y0 > maxY) {
return false;
}
} else {
let ta = (minY - y0) / dy;
let tb = (maxY - y0) / dy;
if (ta > tb) {
const tmp = ta;
ta = tb;
tb = tmp;
}
if (ta > t0) {
t0 = ta;
}
if (tb < t1) {
t1 = tb;
}
if (t0 > t1) {
return false;
}
}
clipSegmentStart = t0;
clipSegmentEnd = t1;
return true;
}
/**
* Clip flat line strings to the given extent. Parts outside the extent are
* dropped and a vertex is inserted where a segment crosses the boundary. A line
* that leaves and re-enters the extent is split into separate parts so that
* positions derived from the result (e.g. labels placed along the line) stay
* within the extent. Output coordinates have a stride of 2.
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {Array<number>} ends Ends.
* @param {number} stride Stride.
* @param {import("../../extent.js").Extent} extent Extent to clip to.
* @return {{flatCoordinates: Array<number>, ends: Array<number>}} Clipped flat
* coordinates and ends.
*/
export function clipFlatLineStrings(flatCoordinates, ends, stride, extent) {
const minX = extent[0];
const minY = extent[1];
const maxX = extent[2];
const maxY = extent[3];
const dest = [];
const destEnds = [];
let open = false;
let lastX, lastY;
let offset = 0;
for (let e = 0, ee = ends.length; e < ee; ++e) {
const end = ends[e];
let prevX = flatCoordinates[offset];
let prevY = flatCoordinates[offset + 1];
let lineHasLast = false;
for (let i = offset + stride; i < end; i += stride) {
const curX = flatCoordinates[i];
const curY = flatCoordinates[i + 1];
if (clipSegment(minX, minY, maxX, maxY, prevX, prevY, curX, curY)) {
const dx = curX - prevX;
const dy = curY - prevY;
const ax = prevX + clipSegmentStart * dx;
const ay = prevY + clipSegmentStart * dy;
const bx = prevX + clipSegmentEnd * dx;
const by = prevY + clipSegmentEnd * dy;
if (open && lineHasLast && ax === lastX && ay === lastY) {
dest.push(bx, by);
} else {
if (open) {
destEnds.push(dest.length);
}
dest.push(ax, ay, bx, by);
open = true;
}
lastX = bx;
lastY = by;
lineHasLast = true;
}
prevX = curX;
prevY = curY;
}
offset = end;
}
if (open) {
destEnds.push(dest.length);
}
return {flatCoordinates: dest, ends: destEnds};
}