Make code prettier

This updates ESLint and our shared eslint-config-openlayers to use Prettier.  Most formatting changes were automatically applied with this:

    npm run lint -- --fix

A few manual changes were required:

 * In `examples/offscreen-canvas.js`, the `//eslint-disable-line` comment needed to be moved to the appropriate line to disable the error about the `'worker-loader!./offscreen-canvas.worker.js'` import.
 * In `examples/webpack/exapmle-builder.js`, spaces could not be added after a couple `function`s for some reason.  While editing this, I reworked `ExampleBuilder` to be a class.
 * In `src/ol/format/WMSGetFeatureInfo.js`, the `// @ts-ignore` comment needed to be moved down one line so it applied to the `parsersNS` argument.
This commit is contained in:
Tim Schaub
2020-04-06 12:25:12 -06:00
parent 53b48baf62
commit 054af09032
790 changed files with 46833 additions and 33765 deletions

View File

@@ -43,7 +43,6 @@ class RenderBox extends Disposable {
* @type {import("../pixel.js").Pixel}
*/
this.endPixel_ = null;
}
/**
@@ -106,9 +105,12 @@ class RenderBox extends Disposable {
startPixel,
[startPixel[0], endPixel[1]],
endPixel,
[endPixel[0], startPixel[1]]
[endPixel[0], startPixel[1]],
];
const coordinates = pixels.map(this.map_.getCoordinateFromPixelInternal, this.map_);
const coordinates = pixels.map(
this.map_.getCoordinateFromPixelInternal,
this.map_
);
// close the polygon
coordinates[4] = coordinates[0].slice();
if (!this.geometry_) {
@@ -126,5 +128,4 @@ class RenderBox extends Disposable {
}
}
export default RenderBox;

View File

@@ -5,7 +5,6 @@
import Event from '../events/Event.js';
class RenderEvent extends Event {
/**
* @param {import("./EventType.js").default} type Type.
* @param {import("../transform.js").Transform=} opt_inversePixelTransform Transform for
@@ -14,7 +13,6 @@ class RenderEvent extends Event {
* @param {?CanvasRenderingContext2D=} opt_context Context.
*/
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context) {
super(type);
/**
@@ -39,9 +37,7 @@ class RenderEvent extends Event {
* @api
*/
this.context = opt_context;
}
}
export default RenderEvent;

View File

@@ -6,7 +6,6 @@
* @enum {string}
*/
export default {
/**
* Triggered before a layer is rendered.
* @event module:ol/render/Event~RenderEvent#prerender
@@ -44,6 +43,5 @@ export default {
* @event module:ol/render/Event~RenderEvent#rendercomplete
* @api
*/
RENDERCOMPLETE: 'rendercomplete'
RENDERCOMPLETE: 'rendercomplete',
};

View File

@@ -1,23 +1,32 @@
/**
* @module ol/render/Feature
*/
import {extend} from '../array.js';
import {createOrUpdateFromCoordinate, createOrUpdateFromFlatCoordinates, getCenter, getHeight} from '../extent.js';
import GeometryType from '../geom/GeometryType.js';
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
import {getInteriorPointOfArray, getInteriorPointsOfMultiArray} from '../geom/flat/interiorpoint.js';
import {interpolatePoint} from '../geom/flat/interpolate.js';
import {
compose as composeTransform,
create as createTransform,
} from '../transform.js';
import {
createOrUpdateFromCoordinate,
createOrUpdateFromFlatCoordinates,
getCenter,
getHeight,
} from '../extent.js';
import {extend} from '../array.js';
import {
getInteriorPointOfArray,
getInteriorPointsOfMultiArray,
} from '../geom/flat/interiorpoint.js';
import {get as getProjection} from '../proj.js';
import {interpolatePoint} from '../geom/flat/interpolate.js';
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
import {transform2D} from '../geom/flat/transform.js';
import {create as createTransform, compose as composeTransform} from '../transform.js';
/**
* @type {import("../transform.js").Transform}
*/
const tmpTransform = createTransform();
/**
* Lightweight, read-only, {@link module:ol/Feature~Feature} and {@link module:ol/geom/Geometry~Geometry} like
* structure, optimized for vector tile rendering and styling. Geometry access
@@ -80,7 +89,6 @@ class RenderFeature {
* @type {Object<string, *>}
*/
this.properties_ = properties;
}
/**
@@ -100,11 +108,15 @@ class RenderFeature {
*/
getExtent() {
if (!this.extent_) {
this.extent_ = this.type_ === GeometryType.POINT ?
createOrUpdateFromCoordinate(this.flatCoordinates_) :
createOrUpdateFromFlatCoordinates(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2);
this.extent_ =
this.type_ === GeometryType.POINT
? createOrUpdateFromCoordinate(this.flatCoordinates_)
: createOrUpdateFromFlatCoordinates(
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2
);
}
return this.extent_;
}
@@ -116,7 +128,13 @@ class RenderFeature {
if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoints_ = getInteriorPointOfArray(
this.flatCoordinates_, 0, /** @type {Array<number>} */ (this.ends_), 2, flatCenter, 0);
this.flatCoordinates_,
0,
/** @type {Array<number>} */ (this.ends_),
2,
flatCenter,
0
);
}
return this.flatInteriorPoints_;
}
@@ -127,9 +145,18 @@ class RenderFeature {
getFlatInteriorPoints() {
if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter(
this.flatCoordinates_, 0, /** @type {Array<Array<number>>} */ (this.ends_), 2);
this.flatCoordinates_,
0,
/** @type {Array<Array<number>>} */ (this.ends_),
2
);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.flatCoordinates_, 0, /** @type {Array<Array<number>>} */ (this.ends_), 2, flatCenters);
this.flatCoordinates_,
0,
/** @type {Array<Array<number>>} */ (this.ends_),
2,
flatCenters
);
}
return this.flatInteriorPoints_;
}
@@ -140,7 +167,12 @@ class RenderFeature {
getFlatMidpoint() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = interpolatePoint(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5);
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2,
0.5
);
}
return this.flatMidpoints_;
}
@@ -156,8 +188,7 @@ class RenderFeature {
const ends = /** @type {Array<number>} */ (this.ends_);
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
flatCoordinates, offset, end, 2, 0.5);
const midpoint = interpolatePoint(flatCoordinates, offset, end, 2, 0.5);
extend(this.flatMidpoints_, midpoint);
offset = end;
}
@@ -255,30 +286,39 @@ class RenderFeature {
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
scale, -scale, 0,
0, 0);
transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2,
tmpTransform, this.flatCoordinates_);
composeTransform(
tmpTransform,
projectedExtent[0],
projectedExtent[3],
scale,
-scale,
0,
0,
0
);
transform2D(
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2,
tmpTransform,
this.flatCoordinates_
);
}
}
/**
* @return {Array<number>|Array<Array<number>>} Ends or endss.
*/
RenderFeature.prototype.getEnds = function() {
RenderFeature.prototype.getEnds = function () {
return this.ends_;
};
RenderFeature.prototype.getEndss = RenderFeature.prototype.getEnds;
/**
* @return {Array<number>} Flat coordinates.
*/
RenderFeature.prototype.getFlatCoordinates =
RenderFeature.prototype.getOrientedFlatCoordinates;
RenderFeature.prototype.getOrientedFlatCoordinates;
export default RenderFeature;

View File

@@ -1,15 +1,14 @@
/**
* @module ol/render/canvas
*/
import {getFontParameters} from '../css.js';
import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js';
import BaseObject from '../Object.js';
import EventTarget from '../events/Target.js';
import {WORKER_OFFSCREEN_CANVAS} from '../has.js';
import {clear} from '../obj.js';
import {createCanvasContext2D} from '../dom.js';
import {getFontParameters} from '../css.js';
import {toString} from '../transform.js';
/**
* @typedef {Object} FillState
* @property {import("../colorlike.js").ColorLike} fillStyle
@@ -43,7 +42,6 @@ import {toString} from '../transform.js';
* @property {number} [miterLimit]
*/
/**
* @typedef {Object} StrokeState
* @property {CanvasLineCap} lineCap
@@ -55,7 +53,6 @@ import {toString} from '../transform.js';
* @property {import("../colorlike.js").ColorLike} strokeStyle
*/
/**
* @typedef {Object} TextState
* @property {string} font
@@ -82,90 +79,77 @@ import {toString} from '../transform.js';
* @typedef {Array<*>} DeclutterGroup
*/
/**
* Declutter groups for support of multi geometries.
* @typedef {Array<DeclutterGroup>} DeclutterGroups
*/
/**
* @const
* @type {string}
*/
export const defaultFont = '10px sans-serif';
/**
* @const
* @type {import("../colorlike.js").ColorLike}
*/
export const defaultFillStyle = '#000';
/**
* @const
* @type {CanvasLineCap}
*/
export const defaultLineCap = 'round';
/**
* @const
* @type {Array<number>}
*/
export const defaultLineDash = [];
/**
* @const
* @type {number}
*/
export const defaultLineDashOffset = 0;
/**
* @const
* @type {CanvasLineJoin}
*/
export const defaultLineJoin = 'round';
/**
* @const
* @type {number}
*/
export const defaultMiterLimit = 10;
/**
* @const
* @type {import("../colorlike.js").ColorLike}
*/
export const defaultStrokeStyle = '#000';
/**
* @const
* @type {string}
*/
export const defaultTextAlign = 'center';
/**
* @const
* @type {string}
*/
export const defaultTextBaseline = 'middle';
/**
* @const
* @type {Array<number>}
*/
export const defaultPadding = [0, 0, 0, 0];
/**
* @const
* @type {number}
@@ -186,7 +170,7 @@ export const checkedFonts = new BaseObject();
* @deprecated
*/
export const labelCache = new EventTarget();
labelCache.setSize = function() {
labelCache.setSize = function () {
console.warn('labelCache is deprecated.'); //eslint-disable-line
};
@@ -205,12 +189,11 @@ let measureFont;
*/
export const textHeights = {};
/**
* Clears the label cache when a font becomes available.
* @param {string} fontSpec CSS font spec.
*/
export const registerFont = (function() {
export const registerFont = (function () {
const retries = 100;
const size = '32px ';
const referenceFonts = ['monospace', 'serif'];
@@ -228,9 +211,22 @@ export const registerFont = (function() {
let available = true;
for (let i = 0; i < len; ++i) {
const referenceFont = referenceFonts[i];
referenceWidth = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + referenceFont, text);
referenceWidth = measureTextWidth(
fontStyle + ' ' + fontWeight + ' ' + size + referenceFont,
text
);
if (fontFamily != referenceFont) {
const width = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont, text);
const width = measureTextWidth(
fontStyle +
' ' +
fontWeight +
' ' +
size +
fontFamily +
',' +
referenceFont,
text
);
// If width and referenceWidth are the same, then the fallback was used
// instead of the font we wanted, so the font is not available.
available = available && width != referenceWidth;
@@ -266,7 +262,7 @@ export const registerFont = (function() {
}
}
return function(fontSpec) {
return function (fontSpec) {
const font = getFontParameters(fontSpec);
if (!font) {
return;
@@ -288,25 +284,28 @@ export const registerFont = (function() {
};
})();
/**
* @param {string} font Font to use for measuring.
* @return {import("../size.js").Size} Measurement.
*/
export const measureTextHeight = (function() {
export const measureTextHeight = (function () {
/**
* @type {HTMLDivElement}
*/
let div;
const heights = textHeights;
return function(fontSpec) {
return function (fontSpec) {
let height = heights[fontSpec];
if (height == undefined) {
if (WORKER_OFFSCREEN_CANVAS) {
const font = getFontParameters(fontSpec);
const metrics = measureText(fontSpec, 'Žg');
const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
const lineHeight = isNaN(Number(font.lineHeight))
? 1.2
: Number(font.lineHeight);
textHeights[fontSpec] =
lineHeight *
(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
} else {
if (!div) {
div = document.createElement('div');
@@ -368,7 +367,6 @@ export function measureAndCacheTextWidth(font, text, cache) {
return width;
}
/**
* @param {string} font Font to use for measuring.
* @param {Array<string>} lines Lines to measure.
@@ -387,7 +385,6 @@ export function measureTextWidths(font, lines, widths) {
return width;
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {number} rotation Rotation.
@@ -402,7 +399,6 @@ export function rotateAtOffset(context, rotation, offsetX, offsetY) {
}
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../transform.js").Transform|null} transform Transform.
@@ -416,8 +412,19 @@ export function rotateAtOffset(context, rotation, offsetX, offsetY) {
* @param {number} y Y.
* @param {number} scale Scale.
*/
export function drawImageOrLabel(context,
transform, opacity, labelOrImage, originX, originY, w, h, x, y, scale) {
export function drawImageOrLabel(
context,
transform,
opacity,
labelOrImage,
originX,
originY,
w,
h,
x,
y,
scale
) {
context.save();
if (opacity !== 1) {
@@ -427,14 +434,24 @@ export function drawImageOrLabel(context,
context.setTransform.apply(context, transform);
}
if ((/** @type {*} */ (labelOrImage).contextInstructions)) {
if (/** @type {*} */ (labelOrImage).contextInstructions) {
// label
context.translate(x, y);
context.scale(scale, scale);
executeLabelInstructions(/** @type {Label} */ (labelOrImage), context);
} else {
// image
context.drawImage(/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage), originX, originY, w, h, x, y, w * scale, h * scale);
context.drawImage(
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage),
originX,
originY,
w,
h,
x,
y,
w * scale,
h * scale
);
}
context.restore();
@@ -448,7 +465,10 @@ function executeLabelInstructions(label, context) {
const contextInstructions = label.contextInstructions;
for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
if (Array.isArray(contextInstructions[i + 1])) {
context[contextInstructions[i]].apply(context, contextInstructions[i + 1]);
context[contextInstructions[i]].apply(
context,
contextInstructions[i + 1]
);
} else {
context[contextInstructions[i]] = contextInstructions[i + 1];
}

View File

@@ -1,18 +1,28 @@
/**
* @module ol/render/canvas/Builder
*/
import {equals, reverseSubArray} from '../../array.js';
import CanvasInstruction from './Instruction.js';
import GeometryType from '../../geom/GeometryType.js';
import Relationship from '../../extent/Relationship.js';
import VectorContext from '../VectorContext.js';
import {asColorLike} from '../../colorlike.js';
import {buffer, clone, coordinateRelationship} from '../../extent.js';
import Relationship from '../../extent/Relationship.js';
import GeometryType from '../../geom/GeometryType.js';
import {inflateCoordinates, inflateCoordinatesArray, inflateMultiCoordinatesArray} from '../../geom/flat/inflate.js';
import VectorContext from '../VectorContext.js';
import {defaultFillStyle, defaultStrokeStyle,
defaultMiterLimit, defaultLineWidth, defaultLineJoin, defaultLineDashOffset,
defaultLineDash, defaultLineCap} from '../canvas.js';
import CanvasInstruction from './Instruction.js';
import {
defaultFillStyle,
defaultLineCap,
defaultLineDash,
defaultLineDashOffset,
defaultLineJoin,
defaultLineWidth,
defaultMiterLimit,
defaultStrokeStyle,
} from '../canvas.js';
import {equals, reverseSubArray} from '../../array.js';
import {
inflateCoordinates,
inflateCoordinatesArray,
inflateMultiCoordinatesArray,
} from '../../geom/flat/inflate.js';
/**
* @typedef {Object} SerializableInstructions
@@ -24,7 +34,6 @@ import CanvasInstruction from './Instruction.js';
* @property {!Object<string, import("../canvas.js").StrokeState>} [strokeStates] The stroke states (decluttering).
*/
class CanvasBuilder extends VectorContext {
/**
* @param {number} tolerance Tolerance.
@@ -114,7 +123,6 @@ class CanvasBuilder extends VectorContext {
* @type {import("../canvas.js").FillStrokeState}
*/
this.state = /** @type {import("../canvas.js").FillStrokeState} */ ({});
}
/**
@@ -124,9 +132,11 @@ class CanvasBuilder extends VectorContext {
*/
applyPixelRatio(dashArray) {
const pixelRatio = this.pixelRatio;
return pixelRatio == 1 ? dashArray : dashArray.map(function(dash) {
return dash * pixelRatio;
});
return pixelRatio == 1
? dashArray
: dashArray.map(function (dash) {
return dash * pixelRatio;
});
}
/**
@@ -139,8 +149,14 @@ class CanvasBuilder extends VectorContext {
* @protected
* @return {number} My end.
*/
appendFlatCoordinates(flatCoordinates, offset, end, stride, closed, skipFirst) {
appendFlatCoordinates(
flatCoordinates,
offset,
end,
stride,
closed,
skipFirst
) {
let myEnd = this.coordinates.length;
const extent = this.getBufferedMaxExtent();
if (skipFirst) {
@@ -195,7 +211,14 @@ class CanvasBuilder extends VectorContext {
drawCustomCoordinates_(flatCoordinates, offset, ends, stride, builderEnds) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const builderEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false);
const builderEnd = this.appendFlatCoordinates(
flatCoordinates,
offset,
end,
stride,
false,
false
);
builderEnds.push(builderEnd);
offset = end;
}
@@ -221,33 +244,79 @@ class CanvasBuilder extends VectorContext {
offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const myEnds = [];
offset = this.drawCustomCoordinates_(flatCoordinates, offset, endss[i], stride, myEnds);
offset = this.drawCustomCoordinates_(
flatCoordinates,
offset,
endss[i],
stride,
myEnds
);
builderEndss.push(myEnds);
}
this.instructions.push([CanvasInstruction.CUSTOM,
builderBegin, builderEndss, geometry, renderer, inflateMultiCoordinatesArray]);
} else if (type == GeometryType.POLYGON || type == GeometryType.MULTI_LINE_STRING) {
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEndss,
geometry,
renderer,
inflateMultiCoordinatesArray,
]);
} else if (
type == GeometryType.POLYGON ||
type == GeometryType.MULTI_LINE_STRING
) {
builderEnds = [];
flatCoordinates = (type == GeometryType.POLYGON) ?
/** @type {import("../../geom/Polygon.js").default} */ (geometry).getOrientedFlatCoordinates() :
geometry.getFlatCoordinates();
offset = this.drawCustomCoordinates_(flatCoordinates, 0,
flatCoordinates =
type == GeometryType.POLYGON
? /** @type {import("../../geom/Polygon.js").default} */ (geometry).getOrientedFlatCoordinates()
: geometry.getFlatCoordinates();
offset = this.drawCustomCoordinates_(
flatCoordinates,
0,
/** @type {import("../../geom/Polygon.js").default|import("../../geom/MultiLineString.js").default} */ (geometry).getEnds(),
stride, builderEnds);
this.instructions.push([CanvasInstruction.CUSTOM,
builderBegin, builderEnds, geometry, renderer, inflateCoordinatesArray]);
} else if (type == GeometryType.LINE_STRING || type == GeometryType.MULTI_POINT) {
stride,
builderEnds
);
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnds,
geometry,
renderer,
inflateCoordinatesArray,
]);
} else if (
type == GeometryType.LINE_STRING ||
type == GeometryType.MULTI_POINT
) {
flatCoordinates = geometry.getFlatCoordinates();
builderEnd = this.appendFlatCoordinates(
flatCoordinates, 0, flatCoordinates.length, stride, false, false);
this.instructions.push([CanvasInstruction.CUSTOM,
builderBegin, builderEnd, geometry, renderer, inflateCoordinates]);
flatCoordinates,
0,
flatCoordinates.length,
stride,
false,
false
);
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
renderer,
inflateCoordinates,
]);
} else if (type == GeometryType.POINT) {
flatCoordinates = geometry.getFlatCoordinates();
this.coordinates.push(flatCoordinates[0], flatCoordinates[1]);
builderEnd = this.coordinates.length;
this.instructions.push([CanvasInstruction.CUSTOM,
builderBegin, builderEnd, geometry, renderer]);
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
renderer,
]);
}
this.endGeometry(feature);
}
@@ -259,9 +328,19 @@ class CanvasBuilder extends VectorContext {
*/
beginGeometry(geometry, feature) {
const extent = geometry.getExtent();
this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0, extent];
this.beginGeometryInstruction1_ = [
CanvasInstruction.BEGIN_GEOMETRY,
feature,
0,
extent,
];
this.instructions.push(this.beginGeometryInstruction1_);
this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0, extent];
this.beginGeometryInstruction2_ = [
CanvasInstruction.BEGIN_GEOMETRY,
feature,
0,
extent,
];
this.hitDetectionInstructions.push(this.beginGeometryInstruction2_);
}
@@ -272,7 +351,7 @@ class CanvasBuilder extends VectorContext {
return {
instructions: this.instructions,
hitDetectionInstructions: this.hitDetectionInstructions,
coordinates: this.coordinates
coordinates: this.coordinates,
};
}
@@ -310,33 +389,41 @@ class CanvasBuilder extends VectorContext {
const state = this.state;
if (fillStyle) {
const fillStyleColor = fillStyle.getColor();
state.fillStyle = asColorLike(fillStyleColor ?
fillStyleColor : defaultFillStyle);
state.fillStyle = asColorLike(
fillStyleColor ? fillStyleColor : defaultFillStyle
);
} else {
state.fillStyle = undefined;
}
if (strokeStyle) {
const strokeStyleColor = strokeStyle.getColor();
state.strokeStyle = asColorLike(strokeStyleColor ?
strokeStyleColor : defaultStrokeStyle);
state.strokeStyle = asColorLike(
strokeStyleColor ? strokeStyleColor : defaultStrokeStyle
);
const strokeStyleLineCap = strokeStyle.getLineCap();
state.lineCap = strokeStyleLineCap !== undefined ?
strokeStyleLineCap : defaultLineCap;
state.lineCap =
strokeStyleLineCap !== undefined ? strokeStyleLineCap : defaultLineCap;
const strokeStyleLineDash = strokeStyle.getLineDash();
state.lineDash = strokeStyleLineDash ?
strokeStyleLineDash.slice() : defaultLineDash;
state.lineDash = strokeStyleLineDash
? strokeStyleLineDash.slice()
: defaultLineDash;
const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset();
state.lineDashOffset = strokeStyleLineDashOffset ?
strokeStyleLineDashOffset : defaultLineDashOffset;
state.lineDashOffset = strokeStyleLineDashOffset
? strokeStyleLineDashOffset
: defaultLineDashOffset;
const strokeStyleLineJoin = strokeStyle.getLineJoin();
state.lineJoin = strokeStyleLineJoin !== undefined ?
strokeStyleLineJoin : defaultLineJoin;
state.lineJoin =
strokeStyleLineJoin !== undefined
? strokeStyleLineJoin
: defaultLineJoin;
const strokeStyleWidth = strokeStyle.getWidth();
state.lineWidth = strokeStyleWidth !== undefined ?
strokeStyleWidth : defaultLineWidth;
state.lineWidth =
strokeStyleWidth !== undefined ? strokeStyleWidth : defaultLineWidth;
const strokeStyleMiterLimit = strokeStyle.getMiterLimit();
state.miterLimit = strokeStyleMiterLimit !== undefined ?
strokeStyleMiterLimit : defaultMiterLimit;
state.miterLimit =
strokeStyleMiterLimit !== undefined
? strokeStyleMiterLimit
: defaultMiterLimit;
if (state.lineWidth > this.maxLineWidth) {
this.maxLineWidth = state.lineWidth;
@@ -383,9 +470,13 @@ class CanvasBuilder extends VectorContext {
createStroke(state) {
return [
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth * this.pixelRatio, state.lineCap,
state.lineJoin, state.miterLimit,
this.applyPixelRatio(state.lineDash), state.lineDashOffset * this.pixelRatio
state.strokeStyle,
state.lineWidth * this.pixelRatio,
state.lineCap,
state.lineJoin,
state.miterLimit,
this.applyPixelRatio(state.lineDash),
state.lineDashOffset * this.pixelRatio,
];
}
@@ -415,13 +506,16 @@ class CanvasBuilder extends VectorContext {
const lineJoin = state.lineJoin;
const lineWidth = state.lineWidth;
const miterLimit = state.miterLimit;
if (state.currentStrokeStyle != strokeStyle ||
state.currentLineCap != lineCap ||
(lineDash != state.currentLineDash && !equals(state.currentLineDash, lineDash)) ||
state.currentLineDashOffset != lineDashOffset ||
state.currentLineJoin != lineJoin ||
state.currentLineWidth != lineWidth ||
state.currentMiterLimit != miterLimit) {
if (
state.currentStrokeStyle != strokeStyle ||
state.currentLineCap != lineCap ||
(lineDash != state.currentLineDash &&
!equals(state.currentLineDash, lineDash)) ||
state.currentLineDashOffset != lineDashOffset ||
state.currentLineJoin != lineJoin ||
state.currentLineWidth != lineWidth ||
state.currentMiterLimit != miterLimit
) {
if (strokeStyle !== undefined) {
applyStroke.call(this, state);
}
@@ -459,7 +553,7 @@ class CanvasBuilder extends VectorContext {
if (!this.bufferedMaxExtent_) {
this.bufferedMaxExtent_ = clone(this.maxExtent);
if (this.maxLineWidth > 0) {
const width = this.resolution * (this.maxLineWidth + 1) / 2;
const width = (this.resolution * (this.maxLineWidth + 1)) / 2;
buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
}
}
@@ -467,5 +561,4 @@ class CanvasBuilder extends VectorContext {
}
}
export default CanvasBuilder;

View File

@@ -2,13 +2,12 @@
* @module ol/render/canvas/BuilderGroup
*/
import {createEmpty} from '../../extent.js';
import Builder from './Builder.js';
import ImageBuilder from './ImageBuilder.js';
import LineStringBuilder from './LineStringBuilder.js';
import PolygonBuilder from './PolygonBuilder.js';
import TextBuilder from './TextBuilder.js';
import {createEmpty} from '../../extent.js';
/**
* @type {Object<import("./BuilderType").default, typeof Builder>}
@@ -19,10 +18,9 @@ const BATCH_CONSTRUCTORS = {
'Image': ImageBuilder,
'LineString': LineStringBuilder,
'Polygon': PolygonBuilder,
'Text': TextBuilder
'Text': TextBuilder,
};
class BuilderGroup {
/**
* @param {number} tolerance Tolerance.
@@ -32,7 +30,6 @@ class BuilderGroup {
* @param {boolean} declutter Decluttering enabled.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio, declutter) {
/**
* @type {boolean}
* @private
@@ -126,8 +123,12 @@ class BuilderGroup {
let replay = replays[builderType];
if (replay === undefined) {
const Constructor = BATCH_CONSTRUCTORS[builderType];
replay = new Constructor(this.tolerance_, this.maxExtent_,
this.resolution_, this.pixelRatio_);
replay = new Constructor(
this.tolerance_,
this.maxExtent_,
this.resolution_,
this.pixelRatio_
);
replays[builderType] = replay;
}
return replay;

View File

@@ -11,5 +11,5 @@ export default {
IMAGE: 'Image',
LINE_STRING: 'LineString',
POLYGON: 'Polygon',
TEXT: 'Text'
TEXT: 'Text',
};

View File

@@ -1,25 +1,38 @@
/**
* @module ol/render/canvas/Executor
*/
import {equals} from '../../array.js';
import {createEmpty, createOrUpdate,
createOrUpdateEmpty, extend, intersects} from '../../extent.js';
import {lineStringLength} from '../../geom/flat/length.js';
import {drawTextOnPath} from '../../geom/flat/textpath.js';
import {transform2D} from '../../geom/flat/transform.js';
import {drawImageOrLabel, defaultPadding, defaultTextBaseline} from '../canvas.js';
import CanvasInstruction from './Instruction.js';
import {TEXT_ALIGN} from './TextBuilder.js';
import {
create as createTransform,
compose as composeTransform,
apply as applyTransform,
setFromArray as transformSetFromArray
} from '../../transform.js';
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import RBush from 'rbush/rbush.js';
import {TEXT_ALIGN} from './TextBuilder.js';
import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
import {
apply as applyTransform,
compose as composeTransform,
create as createTransform,
setFromArray as transformSetFromArray,
} from '../../transform.js';
import {
createEmpty,
createOrUpdate,
createOrUpdateEmpty,
extend,
intersects,
} from '../../extent.js';
import {
defaultPadding,
defaultTextBaseline,
drawImageOrLabel,
} from '../canvas.js';
import {
defaultTextAlign,
measureAndCacheTextWidth,
measureTextHeight,
measureTextWidths,
} from '../canvas.js';
import {drawTextOnPath} from '../../geom/flat/textpath.js';
import {equals} from '../../array.js';
import {lineStringLength} from '../../geom/flat/length.js';
import {transform2D} from '../../geom/flat/transform.js';
/**
* @typedef {Object} SerializableInstructions
@@ -50,7 +63,6 @@ const p3 = [];
/** @type {import("../../coordinate.js").Coordinate} */
const p4 = [];
class Executor {
/**
* @param {number} resolution Resolution.
@@ -59,7 +71,6 @@ class Executor {
* @param {SerializableInstructions} instructions The serializable instructions
*/
constructor(resolution, pixelRatio, overlaps, instructions) {
/**
* @protected
* @type {boolean}
@@ -178,7 +189,8 @@ class Executor {
const pixelRatio = this.pixelRatio;
const scale = textState.scale * pixelRatio;
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
const strokeWidth =
strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
const lines = text.split('\n');
const numLines = lines.length;
@@ -193,7 +205,7 @@ class Executor {
// make canvas 2 pixels wider to account for italic text width measurement errors
width: Math.ceil((renderWidth + 2) * scale),
height: Math.ceil((height + strokeWidth) * scale),
contextInstructions: contextInstructions
contextInstructions: contextInstructions,
};
if (scale != 1) {
contextInstructions.push('scale', [scale, scale]);
@@ -217,17 +229,25 @@ class Executor {
}
contextInstructions.push('textBaseline', 'middle');
contextInstructions.push('textAlign', 'center');
const leftRight = (0.5 - align);
const leftRight = 0.5 - align;
const x = align * renderWidth + leftRight * strokeWidth;
let i;
if (strokeKey) {
for (i = 0; i < numLines; ++i) {
contextInstructions.push('strokeText', [lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight]);
contextInstructions.push('strokeText', [
lines[i],
x + leftRight * widths[i],
0.5 * (strokeWidth + lineHeight) + i * lineHeight,
]);
}
}
if (fillKey) {
for (i = 0; i < numLines; ++i) {
contextInstructions.push('fillText', [lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight]);
contextInstructions.push('fillText', [
lines[i],
x + leftRight * widths[i],
0.5 * (strokeWidth + lineHeight) + i * lineHeight,
]);
}
}
this.labels_[key] = label;
@@ -243,7 +263,15 @@ class Executor {
* @param {Array<*>} fillInstruction Fill instruction.
* @param {Array<*>} strokeInstruction Stroke instruction.
*/
replayTextBackground_(context, p1, p2, p3, p4, fillInstruction, strokeInstruction) {
replayTextBackground_(
context,
p1,
p2,
p3,
p4,
fillInstruction,
strokeInstruction
) {
context.beginPath();
context.moveTo.apply(context, p1);
context.lineTo.apply(context, p2);
@@ -255,7 +283,10 @@ class Executor {
this.fill_(context);
}
if (strokeInstruction) {
this.setStrokeStyle_(context, /** @type {Array<*>} */ (strokeInstruction));
this.setStrokeStyle_(
context,
/** @type {Array<*>} */ (strokeInstruction)
);
context.stroke();
}
}
@@ -306,8 +337,14 @@ class Executor {
x -= anchorX;
y -= anchorY;
const w = (width + originX > imageOrLabel.width) ? imageOrLabel.width - originX : width;
const h = (height + originY > imageOrLabel.height) ? imageOrLabel.height - originY : height;
const w =
width + originX > imageOrLabel.width
? imageOrLabel.width - originX
: width;
const h =
height + originY > imageOrLabel.height
? imageOrLabel.height - originY
: height;
const boxW = padding[3] + w * scale + padding[1];
const boxH = padding[0] + h * scale + padding[2];
const boxX = x - padding[3];
@@ -328,7 +365,16 @@ class Executor {
if (rotation !== 0) {
const centerX = x + anchorX;
const centerY = y + anchorY;
transform = composeTransform(tmpTransform, centerX, centerY, 1, 1, rotation, -centerX, -centerY);
transform = composeTransform(
tmpTransform,
centerX,
centerY,
1,
1,
rotation,
-centerX,
-centerY
);
applyTransform(tmpTransform, p1);
applyTransform(tmpTransform, p2);
@@ -345,10 +391,14 @@ class Executor {
createOrUpdate(boxX, boxY, boxX + boxW, boxY + boxH, tmpExtent);
}
const canvas = context.canvas;
const strokePadding = strokeInstruction ? (strokeInstruction[2] * scale / 2) : 0;
const strokePadding = strokeInstruction
? (strokeInstruction[2] * scale) / 2
: 0;
const intersects =
tmpExtent[0] - strokePadding <= canvas.width && tmpExtent[2] + strokePadding >= 0 &&
tmpExtent[1] - strokePadding <= canvas.height && tmpExtent[3] + strokePadding >= 0;
tmpExtent[0] - strokePadding <= canvas.width &&
tmpExtent[2] + strokePadding >= 0 &&
tmpExtent[1] - strokePadding <= canvas.height &&
tmpExtent[3] + strokePadding >= 0;
if (snapToPixel) {
x = Math.round(x);
@@ -360,22 +410,59 @@ class Executor {
return;
}
extend(declutterGroup, tmpExtent);
const declutterArgs = intersects ?
[context, transform ? transform.slice(0) : null, opacity, imageOrLabel, originX, originY, w, h, x, y, scale] :
null;
const declutterArgs = intersects
? [
context,
transform ? transform.slice(0) : null,
opacity,
imageOrLabel,
originX,
originY,
w,
h,
x,
y,
scale,
]
: null;
if (declutterArgs) {
if (fillStroke) {
declutterArgs.push(fillInstruction, strokeInstruction, p1.slice(0), p2.slice(0), p3.slice(0), p4.slice(0));
declutterArgs.push(
fillInstruction,
strokeInstruction,
p1.slice(0),
p2.slice(0),
p3.slice(0),
p4.slice(0)
);
}
declutterGroup.push(declutterArgs);
}
} else if (intersects) {
if (fillStroke) {
this.replayTextBackground_(context, p1, p2, p3, p4,
this.replayTextBackground_(
context,
p1,
p2,
p3,
p4,
/** @type {Array<*>} */ (fillInstruction),
/** @type {Array<*>} */ (strokeInstruction));
/** @type {Array<*>} */ (strokeInstruction)
);
}
drawImageOrLabel(context, transform, opacity, imageOrLabel, originX, originY, w, h, x, y, scale);
drawImageOrLabel(
context,
transform,
opacity,
imageOrLabel,
originX,
originY,
w,
h,
x,
y,
scale
);
}
}
@@ -431,7 +518,7 @@ class Executor {
minY: /** @type {number} */ (declutterGroup[1]),
maxX: /** @type {number} */ (declutterGroup[2]),
maxY: /** @type {number} */ (declutterGroup[3]),
value: feature
value: feature,
};
if (!declutterTree) {
declutterTree = new RBush(9);
@@ -446,9 +533,15 @@ class Executor {
context.globalAlpha = opacity;
}
if (declutterData.length > 11) {
this.replayTextBackground_(declutterData[0],
declutterData[13], declutterData[14], declutterData[15], declutterData[16],
declutterData[11], declutterData[12]);
this.replayTextBackground_(
declutterData[0],
declutterData[13],
declutterData[14],
declutterData[15],
declutterData[16],
declutterData[11],
declutterData[12]
);
}
drawImageOrLabel.apply(undefined, declutterData);
if (currentAlpha !== opacity) {
@@ -480,17 +573,20 @@ class Executor {
const pixelRatio = this.pixelRatio;
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
const strokeWidth =
strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
// Remove the 2 pixels we added in createLabel() for the anchor
const width = label.width / pixelRatio - 2 * textState.scale;
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth;
const anchorY =
(baseline * label.height) / pixelRatio +
2 * (0.5 - baseline) * strokeWidth;
return {
label: label,
anchorX: anchorX,
anchorY: anchorY
anchorY: anchorY,
};
}
@@ -524,15 +620,30 @@ class Executor {
this.pixelCoordinates_ = [];
}
pixelCoordinates = transform2D(
this.coordinates, 0, this.coordinates.length, 2,
transform, this.pixelCoordinates_);
this.coordinates,
0,
this.coordinates.length,
2,
transform,
this.pixelCoordinates_
);
transformSetFromArray(this.renderedTransform_, transform);
}
let i = 0; // instruction index
const ii = instructions.length; // end of instructions
let d = 0; // data index
let dd; // end of per-instruction data
let anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, declutterGroups, image, text, textKey;
let anchorX,
anchorY,
prevX,
prevY,
roundX,
roundY,
declutterGroup,
declutterGroups,
image,
text,
textKey;
let strokeKey, fillKey;
let pendingFill = 0;
let pendingStroke = 0;
@@ -540,18 +651,20 @@ class Executor {
let lastStrokeInstruction = null;
const coordinateCache = this.coordinateCache_;
const viewRotation = this.viewRotation_;
const viewRotationFromTransform = Math.round(Math.atan2(-transform[1], transform[0]) * 1e12) / 1e12;
const viewRotationFromTransform =
Math.round(Math.atan2(-transform[1], transform[0]) * 1e12) / 1e12;
const state = /** @type {import("../../render.js").State} */ ({
context: context,
pixelRatio: this.pixelRatio,
resolution: this.resolution,
rotation: viewRotation
rotation: viewRotation,
});
// When the batch size gets too big, performance decreases. 200 is a good
// balance between batch size and number of fill/stroke instructions.
const batchSize = this.instructions != instructions || this.overlaps ? 0 : 200;
const batchSize =
this.instructions != instructions || this.overlaps ? 0 : 200;
let /** @type {import("../../Feature.js").FeatureLike} */ feature;
let x, y;
while (i < ii) {
@@ -562,7 +675,10 @@ class Executor {
feature = /** @type {import("../../Feature.js").FeatureLike} */ (instruction[1]);
if (!feature.getGeometry()) {
i = /** @type {number} */ (instruction[2]);
} else if (opt_hitExtent !== undefined && !intersects(opt_hitExtent, instruction[3])) {
} else if (
opt_hitExtent !== undefined &&
!intersects(opt_hitExtent, instruction[3])
) {
i = /** @type {number} */ (instruction[2]) + 1;
} else {
++i;
@@ -647,7 +763,12 @@ class Executor {
textKey = /** @type {string} */ (instruction[19]);
strokeKey = /** @type {string} */ (instruction[20]);
fillKey = /** @type {string} */ (instruction[21]);
const labelWithAnchor = this.drawLabelWithPointPlacement_(text, textKey, strokeKey, fillKey);
const labelWithAnchor = this.drawLabelWithPointPlacement_(
text,
textKey,
strokeKey,
fillKey
);
image = labelWithAnchor.label;
instruction[3] = image;
const textOffsetX = /** @type {number} */ (instruction[22]);
@@ -688,7 +809,10 @@ class Executor {
let widthIndex = 0;
let declutterGroupIndex = 0;
for (; d < dd; d += 2) {
if (geometryWidths && geometryWidths[widthIndex++] < width / this.pixelRatio) {
if (
geometryWidths &&
geometryWidths[widthIndex++] < width / this.pixelRatio
) {
continue;
}
if (declutterGroups) {
@@ -700,18 +824,35 @@ class Executor {
}
declutterGroup = declutterGroups[index];
}
this.replayImageOrLabel_(context,
pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY,
declutterGroup, height, opacity, originX, originY, rotation, scale,
snapToPixel, width, padding,
backgroundFill ? /** @type {Array<*>} */ (lastFillInstruction) : null,
backgroundStroke ? /** @type {Array<*>} */ (lastStrokeInstruction) : null);
this.replayImageOrLabel_(
context,
pixelCoordinates[d],
pixelCoordinates[d + 1],
image,
anchorX,
anchorY,
declutterGroup,
height,
opacity,
originX,
originY,
rotation,
scale,
snapToPixel,
width,
padding,
backgroundFill
? /** @type {Array<*>} */ (lastFillInstruction)
: null,
backgroundStroke
? /** @type {Array<*>} */ (lastStrokeInstruction)
: null
);
if (declutterGroup) {
if (declutterGroupIndex === Math.floor(declutterGroupIndex)) {
this.declutterItems.push(this, declutterGroup, feature);
}
declutterGroupIndex += 1 / declutterGroup[4];
}
}
++i;
@@ -745,12 +886,24 @@ class Executor {
}
const pathLength = lineStringLength(pixelCoordinates, begin, end, 2);
const textLength = textScale * measureAndCacheTextWidth(font, text, cachedWidths);
const textLength =
textScale * measureAndCacheTextWidth(font, text, cachedWidths);
if (overflow || textLength <= pathLength) {
const textAlign = this.textStates[textKey].textAlign;
const startM = (pathLength - textLength) * TEXT_ALIGN[textAlign];
const parts = drawTextOnPath(
pixelCoordinates, begin, end, 2, text, startM, maxAngle, textScale, measureAndCacheTextWidth, font, cachedWidths);
pixelCoordinates,
begin,
end,
2,
text,
startM,
maxAngle,
textScale,
measureAndCacheTextWidth,
font,
cachedWidths
);
if (parts) {
let c, cc, chars, label, part;
if (strokeKey) {
@@ -759,12 +912,30 @@ class Executor {
chars = /** @type {string} */ (part[4]);
label = this.createLabel(chars, textKey, '', strokeKey);
anchorX = /** @type {number} */ (part[2]) + strokeWidth;
anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY;
this.replayImageOrLabel_(context,
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
/** @type {number} */ (part[3]), pixelRatioScale, false, label.width,
defaultPadding, null, null);
anchorY =
baseline * label.height +
(0.5 - baseline) * 2 * strokeWidth -
offsetY;
this.replayImageOrLabel_(
context,
/** @type {number} */ (part[0]),
/** @type {number} */ (part[1]),
label,
anchorX,
anchorY,
declutterGroup,
label.height,
1,
0,
0,
/** @type {number} */ (part[3]),
pixelRatioScale,
false,
label.width,
defaultPadding,
null,
null
);
}
}
if (fillKey) {
@@ -774,11 +945,26 @@ class Executor {
label = this.createLabel(chars, textKey, fillKey, '');
anchorX = /** @type {number} */ (part[2]);
anchorY = baseline * label.height - offsetY;
this.replayImageOrLabel_(context,
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
/** @type {number} */ (part[3]), pixelRatioScale, false, label.width,
defaultPadding, null, null);
this.replayImageOrLabel_(
context,
/** @type {number} */ (part[0]),
/** @type {number} */ (part[1]),
label,
anchorX,
anchorY,
declutterGroup,
label.height,
1,
0,
0,
/** @type {number} */ (part[3]),
pixelRatioScale,
false,
label.width,
defaultPadding,
null,
null
);
}
}
}
@@ -884,7 +1070,14 @@ class Executor {
*/
execute(context, transform, viewRotation, snapToPixel) {
this.viewRotation_ = viewRotation;
this.execute_(context, transform, this.instructions, snapToPixel, undefined, undefined);
this.execute_(
context,
transform,
this.instructions,
snapToPixel,
undefined,
undefined
);
}
/**
@@ -906,10 +1099,15 @@ class Executor {
opt_hitExtent
) {
this.viewRotation_ = viewRotation;
return this.execute_(context, transform,
this.hitDetectionInstructions, true, opt_featureCallback, opt_hitExtent);
return this.execute_(
context,
transform,
this.hitDetectionInstructions,
true,
opt_featureCallback,
opt_hitExtent
);
}
}
export default Executor;

View File

@@ -2,14 +2,17 @@
* @module ol/render/canvas/ExecutorGroup
*/
import {numberSafeCompareFunction} from '../../array.js';
import {createCanvasContext2D} from '../../dom.js';
import {buffer, createEmpty, extendCoordinate} from '../../extent.js';
import {transform2D} from '../../geom/flat/transform.js';
import {isEmpty} from '../../obj.js';
import BuilderType from './BuilderType.js';
import {create as createTransform, compose as composeTransform} from '../../transform.js';
import Executor from './Executor.js';
import {buffer, createEmpty, extendCoordinate} from '../../extent.js';
import {
compose as composeTransform,
create as createTransform,
} from '../../transform.js';
import {createCanvasContext2D} from '../../dom.js';
import {isEmpty} from '../../obj.js';
import {numberSafeCompareFunction} from '../../array.js';
import {transform2D} from '../../geom/flat/transform.js';
/**
* @const
@@ -21,10 +24,9 @@ const ORDER = [
BuilderType.LINE_STRING,
BuilderType.IMAGE,
BuilderType.TEXT,
BuilderType.DEFAULT
BuilderType.DEFAULT,
];
class ExecutorGroup {
/**
* @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a
@@ -38,8 +40,14 @@ class ExecutorGroup {
* The serializable instructions.
* @param {number=} opt_renderBuffer Optional rendering buffer.
*/
constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) {
constructor(
maxExtent,
resolution,
pixelRatio,
overlaps,
allInstructions,
opt_renderBuffer
) {
/**
* @private
* @type {import("../../extent.js").Extent}
@@ -121,12 +129,15 @@ class ExecutorGroup {
for (const builderType in instructionByZindex) {
const instructions = instructionByZindex[builderType];
executors[builderType] = new Executor(
this.resolution_, this.pixelRatio_, this.overlaps_, instructions);
this.resolution_,
this.pixelRatio_,
this.overlaps_,
instructions
);
}
}
}
/**
* @param {Array<BuilderType>} executors Executors.
* @return {boolean} Has executors of the provided types.
@@ -143,7 +154,6 @@ class ExecutorGroup {
return false;
}
/**
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
@@ -162,21 +172,31 @@ class ExecutorGroup {
callback,
declutteredFeatures
) {
hitTolerance = Math.round(hitTolerance);
const contextSize = hitTolerance * 2 + 1;
const transform = composeTransform(this.hitDetectionTransform_,
hitTolerance + 0.5, hitTolerance + 0.5,
1 / resolution, -1 / resolution,
const transform = composeTransform(
this.hitDetectionTransform_,
hitTolerance + 0.5,
hitTolerance + 0.5,
1 / resolution,
-1 / resolution,
-rotation,
-coordinate[0], -coordinate[1]);
-coordinate[0],
-coordinate[1]
);
if (!this.hitDetectionContext_) {
this.hitDetectionContext_ = createCanvasContext2D(contextSize, contextSize);
this.hitDetectionContext_ = createCanvasContext2D(
contextSize,
contextSize
);
}
const context = this.hitDetectionContext_;
if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
if (
context.canvas.width !== contextSize ||
context.canvas.height !== contextSize
) {
context.canvas.width = contextSize;
context.canvas.height = contextSize;
} else {
@@ -190,7 +210,11 @@ class ExecutorGroup {
if (this.renderBuffer_ !== undefined) {
hitExtent = createEmpty();
extendCoordinate(hitExtent, coordinate);
buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent);
buffer(
hitExtent,
resolution * (this.renderBuffer_ + hitTolerance),
hitExtent
);
}
const mask = getCircleArray(hitTolerance);
@@ -202,14 +226,21 @@ class ExecutorGroup {
* @return {?} Callback result.
*/
function featureCallback(feature) {
const imageData = context.getImageData(0, 0, contextSize, contextSize).data;
const imageData = context.getImageData(0, 0, contextSize, contextSize)
.data;
for (let i = 0; i < contextSize; i++) {
for (let j = 0; j < contextSize; j++) {
if (mask[i][j]) {
if (imageData[(j * contextSize + i) * 4 + 3] > 0) {
let result;
if (!(declutteredFeatures && (builderType == BuilderType.IMAGE || builderType == BuilderType.TEXT)) ||
declutteredFeatures.indexOf(feature) !== -1) {
if (
!(
declutteredFeatures &&
(builderType == BuilderType.IMAGE ||
builderType == BuilderType.TEXT)
) ||
declutteredFeatures.indexOf(feature) !== -1
) {
result = callback(feature);
}
if (result) {
@@ -236,7 +267,13 @@ class ExecutorGroup {
builderType = ORDER[j];
executor = executors[builderType];
if (executor !== undefined) {
result = executor.executeHitDetection(context, transform, rotation, featureCallback, hitExtent);
result = executor.executeHitDetection(
context,
transform,
rotation,
featureCallback,
hitExtent
);
if (result) {
return result;
}
@@ -260,8 +297,7 @@ class ExecutorGroup {
const maxX = maxExtent[2];
const maxY = maxExtent[3];
const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
transform2D(
flatClipCoords, 0, 8, 2, transform, flatClipCoords);
transform2D(flatClipCoords, 0, 8, 2, transform, flatClipCoords);
return flatClipCoords;
}
@@ -281,8 +317,14 @@ class ExecutorGroup {
* Default is {@link module:ol/render/replay~ORDER}
* @param {Object<string, import("../canvas.js").DeclutterGroup>=} opt_declutterReplays Declutter replays.
*/
execute(context, transform, viewRotation, snapToPixel, opt_builderTypes, opt_declutterReplays) {
execute(
context,
transform,
viewRotation,
snapToPixel,
opt_builderTypes,
opt_declutterReplays
) {
/** @type {Array<number>} */
const zs = Object.keys(this.executorsByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
@@ -303,8 +345,11 @@ class ExecutorGroup {
const builderType = builderTypes[j];
replay = replays[builderType];
if (replay !== undefined) {
if (opt_declutterReplays &&
(builderType == BuilderType.IMAGE || builderType == BuilderType.TEXT)) {
if (
opt_declutterReplays &&
(builderType == BuilderType.IMAGE ||
builderType == BuilderType.TEXT)
) {
const declutter = opt_declutterReplays[zIndexKey];
if (!declutter) {
opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)];
@@ -324,17 +369,15 @@ class ExecutorGroup {
}
}
/**
* This cache is used for storing calculated pixel circles for increasing performance.
* It is a static property to allow each Replaygroup to access it.
* @type {Object<number, Array<Array<(boolean|undefined)>>>}
*/
const circleArrayCache = {
0: [[true]]
0: [[true]],
};
/**
* This method fills a row in the array from the given coordinate to the
* middle with `true`.
@@ -356,7 +399,6 @@ function fillCircleArrayRowToMiddle(array, x, y) {
}
}
/**
* This methods creates a circle inside a fitting array. Points inside the
* circle are marked by true, points on the outside are undefined.
@@ -402,7 +444,6 @@ export function getCircleArray(radius) {
return arr;
}
/**
* @param {!Object<string, Array<*>>} declutterReplays Declutter replays.
* @param {CanvasRenderingContext2D} context Context.
@@ -411,18 +452,27 @@ export function getCircleArray(radius) {
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
* @param {Array<import("../../PluggableMap.js").DeclutterItems>} declutterItems Declutter items.
*/
export function replayDeclutter(declutterReplays, context, rotation, opacity, snapToPixel, declutterItems) {
const zs = Object.keys(declutterReplays).map(Number).sort(numberSafeCompareFunction);
export function replayDeclutter(
declutterReplays,
context,
rotation,
opacity,
snapToPixel,
declutterItems
) {
const zs = Object.keys(declutterReplays)
.map(Number)
.sort(numberSafeCompareFunction);
for (let z = 0, zz = zs.length; z < zz; ++z) {
const executorData = declutterReplays[zs[z].toString()];
let currentExecutor;
for (let i = 0, ii = executorData.length; i < ii;) {
for (let i = 0, ii = executorData.length; i < ii; ) {
const executor = executorData[i++];
if (executor !== currentExecutor) {
currentExecutor = executor;
declutterItems.push({
items: executor.declutterItems,
opacity: opacity
opacity: opacity,
});
}
const transform = executorData[i++];
@@ -431,5 +481,4 @@ export function replayDeclutter(declutterReplays, context, rotation, opacity, sn
}
}
export default ExecutorGroup;

View File

@@ -1,8 +1,8 @@
/**
* @module ol/render/canvas/ImageBuilder
*/
import CanvasInstruction from './Instruction.js';
import CanvasBuilder from './Builder.js';
import CanvasInstruction from './Instruction.js';
class CanvasImageBuilder extends CanvasBuilder {
/**
@@ -91,7 +91,6 @@ class CanvasImageBuilder extends CanvasBuilder {
* @type {number|undefined}
*/
this.width_ = undefined;
}
/**
@@ -103,7 +102,14 @@ class CanvasImageBuilder extends CanvasBuilder {
* @return {number} My end.
*/
drawCoordinates_(flatCoordinates, offset, end, stride) {
return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false);
return this.appendFlatCoordinates(
flatCoordinates,
offset,
end,
stride,
false,
false
);
}
/**
@@ -118,20 +124,47 @@ class CanvasImageBuilder extends CanvasBuilder {
const flatCoordinates = pointGeometry.getFlatCoordinates();
const stride = pointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
const myEnd = this.drawCoordinates_(
flatCoordinates,
0,
flatCoordinates.length,
stride
);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.width_
this.anchorX_,
this.anchorY_,
this.declutterGroups_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_ * this.pixelRatio,
this.width_,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_
this.anchorX_,
this.anchorY_,
this.declutterGroups_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_,
this.width_,
]);
this.endGeometry(feature);
}
@@ -149,20 +182,46 @@ class CanvasImageBuilder extends CanvasBuilder {
const stride = multiPointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(
flatCoordinates, 0, flatCoordinates.length, stride);
flatCoordinates,
0,
flatCoordinates.length,
stride
);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.width_
this.anchorX_,
this.anchorY_,
this.declutterGroups_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_ * this.pixelRatio,
this.width_,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroups_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.width_
this.anchorX_,
this.anchorY_,
this.declutterGroups_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_,
this.width_,
]);
this.endGeometry(feature);
}
@@ -214,5 +273,4 @@ class CanvasImageBuilder extends CanvasBuilder {
}
}
export default CanvasImageBuilder;

View File

@@ -5,16 +5,30 @@
// FIXME need to handle large thick features (where pixel size matters)
// FIXME add offset and end to ol/geom/flat/transform~transform2D?
import {equals} from '../../array.js';
import {asColorLike} from '../../colorlike.js';
import {intersects} from '../../extent.js';
import GeometryType from '../../geom/GeometryType.js';
import {transformGeom2D} from '../../geom/SimpleGeometry.js';
import {transform2D} from '../../geom/flat/transform.js';
import VectorContext from '../VectorContext.js';
import {defaultTextAlign, defaultFillStyle, defaultLineCap, defaultLineDash, defaultLineDashOffset, defaultLineJoin, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline, defaultFont} from '../canvas.js';
import {create as createTransform, compose as composeTransform} from '../../transform.js';
import {asColorLike} from '../../colorlike.js';
import {
compose as composeTransform,
create as createTransform,
} from '../../transform.js';
import {
defaultFillStyle,
defaultFont,
defaultLineCap,
defaultLineDash,
defaultLineDashOffset,
defaultLineJoin,
defaultLineWidth,
defaultMiterLimit,
defaultStrokeStyle,
defaultTextAlign,
defaultTextBaseline,
} from '../canvas.js';
import {equals} from '../../array.js';
import {intersects} from '../../extent.js';
import {transform2D} from '../../geom/flat/transform.js';
import {transformGeom2D} from '../../geom/SimpleGeometry.js';
/**
* @classdesc
@@ -35,7 +49,15 @@ class CanvasImmediateRenderer extends VectorContext {
* @param {number=} opt_squaredTolerance Optional squared tolerance for simplification.
* @param {import("../../proj.js").TransformFunction=} opt_userTransform Transform from user to view projection.
*/
constructor(context, pixelRatio, extent, transform, viewRotation, opt_squaredTolerance, opt_userTransform) {
constructor(
context,
pixelRatio,
extent,
transform,
viewRotation,
opt_squaredTolerance,
opt_userTransform
) {
super();
/**
@@ -241,7 +263,6 @@ class CanvasImmediateRenderer extends VectorContext {
* @type {import("../../transform.js").Transform}
*/
this.tmpLocalTransform_ = createTransform();
}
/**
@@ -256,8 +277,13 @@ class CanvasImmediateRenderer extends VectorContext {
return;
}
const pixelCoordinates = transform2D(
flatCoordinates, offset, end, 2, this.transform_,
this.pixelCoordinates_);
flatCoordinates,
offset,
end,
2,
this.transform_,
this.pixelCoordinates_
);
const context = this.context_;
const localTransform = this.tmpLocalTransform_;
const alpha = context.globalAlpha;
@@ -274,16 +300,29 @@ class CanvasImmediateRenderer extends VectorContext {
if (rotation !== 0 || this.imageScale_ != 1) {
const centerX = x + this.imageAnchorX_;
const centerY = y + this.imageAnchorY_;
composeTransform(localTransform,
centerX, centerY,
this.imageScale_, this.imageScale_,
composeTransform(
localTransform,
centerX,
centerY,
this.imageScale_,
this.imageScale_,
rotation,
-centerX, -centerY);
-centerX,
-centerY
);
context.setTransform.apply(context, localTransform);
}
context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_,
this.imageWidth_, this.imageHeight_, x, y,
this.imageWidth_, this.imageHeight_);
context.drawImage(
this.image_,
this.imageOriginX_,
this.imageOriginY_,
this.imageWidth_,
this.imageHeight_,
x,
y,
this.imageWidth_,
this.imageHeight_
);
}
if (rotation !== 0 || this.imageScale_ != 1) {
context.setTransform(1, 0, 0, 1, 0, 0);
@@ -312,8 +351,13 @@ class CanvasImmediateRenderer extends VectorContext {
}
this.setContextTextState_(this.textState_);
const pixelCoordinates = transform2D(
flatCoordinates, offset, end, stride, this.transform_,
this.pixelCoordinates_);
flatCoordinates,
offset,
end,
stride,
this.transform_,
this.pixelCoordinates_
);
const context = this.context_;
let rotation = this.textRotation_;
if (this.textRotateWithView_) {
@@ -323,11 +367,16 @@ class CanvasImmediateRenderer extends VectorContext {
const x = pixelCoordinates[offset] + this.textOffsetX_;
const y = pixelCoordinates[offset + 1] + this.textOffsetY_;
if (rotation !== 0 || this.textScale_ != 1) {
const localTransform = composeTransform(this.tmpLocalTransform_,
x, y,
this.textScale_, this.textScale_,
const localTransform = composeTransform(
this.tmpLocalTransform_,
x,
y,
this.textScale_,
this.textScale_,
rotation,
-x, -y);
-x,
-y
);
context.setTransform.apply(context, localTransform);
}
if (this.textStrokeState_) {
@@ -354,8 +403,13 @@ class CanvasImmediateRenderer extends VectorContext {
moveToLineTo_(flatCoordinates, offset, end, stride, close) {
const context = this.context_;
const pixelCoordinates = transform2D(
flatCoordinates, offset, end, stride, this.transform_,
this.pixelCoordinates_);
flatCoordinates,
offset,
end,
stride,
this.transform_,
this.pixelCoordinates_
);
context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
let length = pixelCoordinates.length;
if (close) {
@@ -380,7 +434,13 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawRings_(flatCoordinates, offset, ends, stride) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, true);
offset = this.moveToLineTo_(
flatCoordinates,
offset,
ends[i],
stride,
true
);
}
return offset;
}
@@ -404,14 +464,22 @@ class CanvasImmediateRenderer extends VectorContext {
this.setContextStrokeState_(this.strokeState_);
}
const pixelCoordinates = transformGeom2D(
geometry, this.transform_, this.pixelCoordinates_);
geometry,
this.transform_,
this.pixelCoordinates_
);
const dx = pixelCoordinates[2] - pixelCoordinates[0];
const dy = pixelCoordinates[3] - pixelCoordinates[1];
const radius = Math.sqrt(dx * dx + dy * dy);
const context = this.context_;
context.beginPath();
context.arc(
pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI);
pixelCoordinates[0],
pixelCoordinates[1],
radius,
0,
2 * Math.PI
);
if (this.fillState_) {
context.fill();
}
@@ -455,28 +523,44 @@ class CanvasImmediateRenderer extends VectorContext {
const type = geometry.getType();
switch (type) {
case GeometryType.POINT:
this.drawPoint(/** @type {import("../../geom/Point.js").default} */ (geometry));
this.drawPoint(
/** @type {import("../../geom/Point.js").default} */ (geometry)
);
break;
case GeometryType.LINE_STRING:
this.drawLineString(/** @type {import("../../geom/LineString.js").default} */ (geometry));
this.drawLineString(
/** @type {import("../../geom/LineString.js").default} */ (geometry)
);
break;
case GeometryType.POLYGON:
this.drawPolygon(/** @type {import("../../geom/Polygon.js").default} */ (geometry));
this.drawPolygon(
/** @type {import("../../geom/Polygon.js").default} */ (geometry)
);
break;
case GeometryType.MULTI_POINT:
this.drawMultiPoint(/** @type {import("../../geom/MultiPoint.js").default} */ (geometry));
this.drawMultiPoint(
/** @type {import("../../geom/MultiPoint.js").default} */ (geometry)
);
break;
case GeometryType.MULTI_LINE_STRING:
this.drawMultiLineString(/** @type {import("../../geom/MultiLineString.js").default} */ (geometry));
this.drawMultiLineString(
/** @type {import("../../geom/MultiLineString.js").default} */ (geometry)
);
break;
case GeometryType.MULTI_POLYGON:
this.drawMultiPolygon(/** @type {import("../../geom/MultiPolygon.js").default} */ (geometry));
this.drawMultiPolygon(
/** @type {import("../../geom/MultiPolygon.js").default} */ (geometry)
);
break;
case GeometryType.GEOMETRY_COLLECTION:
this.drawGeometryCollection(/** @type {import("../../geom/GeometryCollection.js").default} */ (geometry));
this.drawGeometryCollection(
/** @type {import("../../geom/GeometryCollection.js").default} */ (geometry)
);
break;
case GeometryType.CIRCLE:
this.drawCircle(/** @type {import("../../geom/Circle.js").default} */ (geometry));
this.drawCircle(
/** @type {import("../../geom/Circle.js").default} */ (geometry)
);
break;
default:
}
@@ -522,7 +606,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawPoint(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/Point.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/Point.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
const flatCoordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
@@ -542,7 +629,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawMultiPoint(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/MultiPoint.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/MultiPoint.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
const flatCoordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
@@ -562,7 +652,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawLineString(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/LineString.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/LineString.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
if (!intersects(this.extent_, geometry.getExtent())) {
return;
@@ -572,8 +665,13 @@ class CanvasImmediateRenderer extends VectorContext {
const context = this.context_;
const flatCoordinates = geometry.getFlatCoordinates();
context.beginPath();
this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,
geometry.getStride(), false);
this.moveToLineTo_(
flatCoordinates,
0,
flatCoordinates.length,
geometry.getStride(),
false
);
context.stroke();
}
if (this.text_ !== '') {
@@ -590,7 +688,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawMultiLineString(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/MultiLineString.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/MultiLineString.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
const geometryExtent = geometry.getExtent();
if (!intersects(this.extent_, geometryExtent)) {
@@ -605,7 +706,13 @@ class CanvasImmediateRenderer extends VectorContext {
const stride = geometry.getStride();
context.beginPath();
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, false);
offset = this.moveToLineTo_(
flatCoordinates,
offset,
ends[i],
stride,
false
);
}
context.stroke();
}
@@ -623,7 +730,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawPolygon(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/Polygon.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/Polygon.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
if (!intersects(this.extent_, geometry.getExtent())) {
return;
@@ -637,8 +747,12 @@ class CanvasImmediateRenderer extends VectorContext {
}
const context = this.context_;
context.beginPath();
this.drawRings_(geometry.getOrientedFlatCoordinates(),
0, /** @type {Array<number>} */ (geometry.getEnds()), geometry.getStride());
this.drawRings_(
geometry.getOrientedFlatCoordinates(),
0,
/** @type {Array<number>} */ (geometry.getEnds()),
geometry.getStride()
);
if (this.fillState_) {
context.fill();
}
@@ -659,7 +773,10 @@ class CanvasImmediateRenderer extends VectorContext {
*/
drawMultiPolygon(geometry) {
if (this.squaredTolerance_) {
geometry = /** @type {import("../../geom/MultiPolygon.js").default} */ (geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_));
geometry = /** @type {import("../../geom/MultiPolygon.js").default} */ (geometry.simplifyTransformed(
this.squaredTolerance_,
this.userTransform_
));
}
if (!intersects(this.extent_, geometry.getExtent())) {
return;
@@ -704,7 +821,7 @@ class CanvasImmediateRenderer extends VectorContext {
if (!contextFillState) {
context.fillStyle = fillState.fillStyle;
this.contextFillState_ = {
fillStyle: fillState.fillStyle
fillStyle: fillState.fillStyle,
};
} else {
if (contextFillState.fillStyle != fillState.fillStyle) {
@@ -738,7 +855,7 @@ class CanvasImmediateRenderer extends VectorContext {
lineJoin: strokeState.lineJoin,
lineWidth: strokeState.lineWidth,
miterLimit: strokeState.miterLimit,
strokeStyle: strokeState.strokeStyle
strokeStyle: strokeState.strokeStyle,
};
} else {
if (contextStrokeState.lineCap != strokeState.lineCap) {
@@ -747,7 +864,9 @@ class CanvasImmediateRenderer extends VectorContext {
}
if (context.setLineDash) {
if (!equals(contextStrokeState.lineDash, strokeState.lineDash)) {
context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash);
context.setLineDash(
(contextStrokeState.lineDash = strokeState.lineDash)
);
}
if (contextStrokeState.lineDashOffset != strokeState.lineDashOffset) {
contextStrokeState.lineDashOffset = strokeState.lineDashOffset;
@@ -780,8 +899,9 @@ class CanvasImmediateRenderer extends VectorContext {
setContextTextState_(textState) {
const context = this.context_;
const contextTextState = this.contextTextState_;
const textAlign = textState.textAlign ?
textState.textAlign : defaultTextAlign;
const textAlign = textState.textAlign
? textState.textAlign
: defaultTextAlign;
if (!contextTextState) {
context.font = textState.font;
context.textAlign = /** @type {CanvasTextAlign} */ (textAlign);
@@ -789,7 +909,7 @@ class CanvasImmediateRenderer extends VectorContext {
this.contextTextState_ = {
font: textState.font,
textAlign: textAlign,
textBaseline: textState.textBaseline
textBaseline: textState.textBaseline,
};
} else {
if (contextTextState.font != textState.font) {
@@ -820,8 +940,9 @@ class CanvasImmediateRenderer extends VectorContext {
} else {
const fillStyleColor = fillStyle.getColor();
this.fillState_ = {
fillStyle: asColorLike(fillStyleColor ?
fillStyleColor : defaultFillStyle)
fillStyle: asColorLike(
fillStyleColor ? fillStyleColor : defaultFillStyle
),
};
}
if (!strokeStyle) {
@@ -835,20 +956,30 @@ class CanvasImmediateRenderer extends VectorContext {
const strokeStyleWidth = strokeStyle.getWidth();
const strokeStyleMiterLimit = strokeStyle.getMiterLimit();
this.strokeState_ = {
lineCap: strokeStyleLineCap !== undefined ?
strokeStyleLineCap : defaultLineCap,
lineDash: strokeStyleLineDash ?
strokeStyleLineDash : defaultLineDash,
lineDashOffset: strokeStyleLineDashOffset ?
strokeStyleLineDashOffset : defaultLineDashOffset,
lineJoin: strokeStyleLineJoin !== undefined ?
strokeStyleLineJoin : defaultLineJoin,
lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ?
strokeStyleWidth : defaultLineWidth),
miterLimit: strokeStyleMiterLimit !== undefined ?
strokeStyleMiterLimit : defaultMiterLimit,
strokeStyle: asColorLike(strokeStyleColor ?
strokeStyleColor : defaultStrokeStyle)
lineCap:
strokeStyleLineCap !== undefined
? strokeStyleLineCap
: defaultLineCap,
lineDash: strokeStyleLineDash ? strokeStyleLineDash : defaultLineDash,
lineDashOffset: strokeStyleLineDashOffset
? strokeStyleLineDashOffset
: defaultLineDashOffset,
lineJoin:
strokeStyleLineJoin !== undefined
? strokeStyleLineJoin
: defaultLineJoin,
lineWidth:
this.pixelRatio_ *
(strokeStyleWidth !== undefined
? strokeStyleWidth
: defaultLineWidth),
miterLimit:
strokeStyleMiterLimit !== undefined
? strokeStyleMiterLimit
: defaultMiterLimit,
strokeStyle: asColorLike(
strokeStyleColor ? strokeStyleColor : defaultStrokeStyle
),
};
}
}
@@ -898,8 +1029,9 @@ class CanvasImmediateRenderer extends VectorContext {
} else {
const textFillStyleColor = textFillStyle.getColor();
this.textFillState_ = {
fillStyle: asColorLike(textFillStyleColor ?
textFillStyleColor : defaultFillStyle)
fillStyle: asColorLike(
textFillStyleColor ? textFillStyleColor : defaultFillStyle
),
};
}
const textStrokeStyle = textStyle.getStroke();
@@ -914,20 +1046,31 @@ class CanvasImmediateRenderer extends VectorContext {
const textStrokeStyleWidth = textStrokeStyle.getWidth();
const textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
this.textStrokeState_ = {
lineCap: textStrokeStyleLineCap !== undefined ?
textStrokeStyleLineCap : defaultLineCap,
lineDash: textStrokeStyleLineDash ?
textStrokeStyleLineDash : defaultLineDash,
lineDashOffset: textStrokeStyleLineDashOffset ?
textStrokeStyleLineDashOffset : defaultLineDashOffset,
lineJoin: textStrokeStyleLineJoin !== undefined ?
textStrokeStyleLineJoin : defaultLineJoin,
lineWidth: textStrokeStyleWidth !== undefined ?
textStrokeStyleWidth : defaultLineWidth,
miterLimit: textStrokeStyleMiterLimit !== undefined ?
textStrokeStyleMiterLimit : defaultMiterLimit,
strokeStyle: asColorLike(textStrokeStyleColor ?
textStrokeStyleColor : defaultStrokeStyle)
lineCap:
textStrokeStyleLineCap !== undefined
? textStrokeStyleLineCap
: defaultLineCap,
lineDash: textStrokeStyleLineDash
? textStrokeStyleLineDash
: defaultLineDash,
lineDashOffset: textStrokeStyleLineDashOffset
? textStrokeStyleLineDashOffset
: defaultLineDashOffset,
lineJoin:
textStrokeStyleLineJoin !== undefined
? textStrokeStyleLineJoin
: defaultLineJoin,
lineWidth:
textStrokeStyleWidth !== undefined
? textStrokeStyleWidth
: defaultLineWidth,
miterLimit:
textStrokeStyleMiterLimit !== undefined
? textStrokeStyleMiterLimit
: defaultMiterLimit,
strokeStyle: asColorLike(
textStrokeStyleColor ? textStrokeStyleColor : defaultStrokeStyle
),
};
}
const textFont = textStyle.getFont();
@@ -940,25 +1083,26 @@ class CanvasImmediateRenderer extends VectorContext {
const textTextAlign = textStyle.getTextAlign();
const textTextBaseline = textStyle.getTextBaseline();
this.textState_ = {
font: textFont !== undefined ?
textFont : defaultFont,
textAlign: textTextAlign !== undefined ?
textTextAlign : defaultTextAlign,
textBaseline: textTextBaseline !== undefined ?
textTextBaseline : defaultTextBaseline
font: textFont !== undefined ? textFont : defaultFont,
textAlign:
textTextAlign !== undefined ? textTextAlign : defaultTextAlign,
textBaseline:
textTextBaseline !== undefined
? textTextBaseline
: defaultTextBaseline,
};
this.text_ = textText !== undefined ? textText : '';
this.textOffsetX_ =
textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0;
textOffsetX !== undefined ? this.pixelRatio_ * textOffsetX : 0;
this.textOffsetY_ =
textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0;
this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false;
textOffsetY !== undefined ? this.pixelRatio_ * textOffsetY : 0;
this.textRotateWithView_ =
textRotateWithView !== undefined ? textRotateWithView : false;
this.textRotation_ = textRotation !== undefined ? textRotation : 0;
this.textScale_ = this.pixelRatio_ * (textScale !== undefined ?
textScale : 1);
this.textScale_ =
this.pixelRatio_ * (textScale !== undefined ? textScale : 1);
}
}
}
export default CanvasImmediateRenderer;

View File

@@ -18,32 +18,27 @@ const Instruction = {
MOVE_TO_LINE_TO: 9,
SET_FILL_STYLE: 10,
SET_STROKE_STYLE: 11,
STROKE: 12
STROKE: 12,
};
/**
* @type {Array<Instruction>}
*/
export const fillInstruction = [Instruction.FILL];
/**
* @type {Array<Instruction>}
*/
export const strokeInstruction = [Instruction.STROKE];
/**
* @type {Array<Instruction>}
*/
export const beginPathInstruction = [Instruction.BEGIN_PATH];
/**
* @type {Array<Instruction>}
*/
export const closePathInstruction = [Instruction.CLOSE_PATH];
export default Instruction;

View File

@@ -1,8 +1,11 @@
/**
* @module ol/render/canvas/LineStringBuilder
*/
import CanvasInstruction, {strokeInstruction, beginPathInstruction} from './Instruction.js';
import CanvasBuilder from './Builder.js';
import CanvasInstruction, {
beginPathInstruction,
strokeInstruction,
} from './Instruction.js';
class CanvasLineStringBuilder extends CanvasBuilder {
/**
@@ -26,8 +29,18 @@ class CanvasLineStringBuilder extends CanvasBuilder {
drawFlatCoordinates_(flatCoordinates, offset, end, stride) {
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(
flatCoordinates, offset, end, stride, false, false);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
flatCoordinates,
offset,
end,
stride,
false,
false
);
const moveToLineToInstruction = [
CanvasInstruction.MOVE_TO_LINE_TO,
myBegin,
myEnd,
];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
return end;
@@ -46,14 +59,27 @@ class CanvasLineStringBuilder extends CanvasBuilder {
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(lineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
this.hitDetectionInstructions.push(
[
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
],
beginPathInstruction
);
const flatCoordinates = lineStringGeometry.getFlatCoordinates();
const stride = lineStringGeometry.getStride();
this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.drawFlatCoordinates_(
flatCoordinates,
0,
flatCoordinates.length,
stride
);
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(feature);
}
@@ -71,17 +97,30 @@ class CanvasLineStringBuilder extends CanvasBuilder {
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(multiLineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
this.hitDetectionInstructions.push(
[
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
],
beginPathInstruction
);
const ends = multiLineStringGeometry.getEnds();
const flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
const stride = multiLineStringGeometry.getStride();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.drawFlatCoordinates_(flatCoordinates, offset, /** @type {number} */ (ends[i]), stride);
offset = this.drawFlatCoordinates_(
flatCoordinates,
offset,
/** @type {number} */ (ends[i]),
stride
);
}
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(feature);
@@ -92,7 +131,10 @@ class CanvasLineStringBuilder extends CanvasBuilder {
*/
finish() {
const state = this.state;
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
if (
state.lastStroke != undefined &&
state.lastStroke != this.coordinates.length
) {
this.instructions.push(strokeInstruction);
}
this.reverseHitDetectionInstructions();
@@ -104,7 +146,10 @@ class CanvasLineStringBuilder extends CanvasBuilder {
* @param {import("../canvas.js").FillStrokeState} state State.
*/
applyStroke(state) {
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
if (
state.lastStroke != undefined &&
state.lastStroke != this.coordinates.length
) {
this.instructions.push(strokeInstruction);
state.lastStroke = this.coordinates.length;
}
@@ -114,5 +159,4 @@ class CanvasLineStringBuilder extends CanvasBuilder {
}
}
export default CanvasLineStringBuilder;

View File

@@ -1,13 +1,15 @@
/**
* @module ol/render/canvas/PolygonBuilder
*/
import {snap} from '../../geom/flat/simplify.js';
import {defaultFillStyle} from '../canvas.js';
import CanvasInstruction, {
fillInstruction, strokeInstruction, beginPathInstruction, closePathInstruction
} from './Instruction.js';
import CanvasBuilder from './Builder.js';
import CanvasInstruction, {
beginPathInstruction,
closePathInstruction,
fillInstruction,
strokeInstruction,
} from './Instruction.js';
import {defaultFillStyle} from '../canvas.js';
import {snap} from '../../geom/flat/simplify.js';
class CanvasPolygonBuilder extends CanvasBuilder {
/**
@@ -38,8 +40,19 @@ class CanvasPolygonBuilder extends CanvasBuilder {
for (let i = 0; i < numEnds; ++i) {
const end = ends[i];
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, true, !stroke);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
const myEnd = this.appendFlatCoordinates(
flatCoordinates,
offset,
end,
stride,
true,
!stroke
);
const moveToLineToInstruction = [
CanvasInstruction.MOVE_TO_LINE_TO,
myBegin,
myEnd,
];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
if (stroke) {
@@ -77,21 +90,32 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const flatCoordinates = circleGeometry.getFlatCoordinates();
const stride = circleGeometry.getStride();
const myBegin = this.coordinates.length;
this.appendFlatCoordinates(
flatCoordinates, 0, flatCoordinates.length, stride, false, false);
flatCoordinates,
0,
flatCoordinates.length,
stride,
false,
false
);
const circleInstruction = [CanvasInstruction.CIRCLE, myBegin];
this.instructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
@@ -122,20 +146,30 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const ends = polygonGeometry.getEnds();
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(flatCoordinates, 0, /** @type {Array<number>} */ (ends), stride);
this.drawFlatCoordinatess_(
flatCoordinates,
0,
/** @type {Array<number>} */ (ends),
stride
);
this.endGeometry(feature);
}
@@ -155,14 +189,19 @@ class CanvasPolygonBuilder extends CanvasBuilder {
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const endss = multiPolygonGeometry.getEndss();
@@ -170,7 +209,12 @@ class CanvasPolygonBuilder extends CanvasBuilder {
const stride = multiPolygonGeometry.getStride();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
offset = this.drawFlatCoordinatess_(
flatCoordinates,
offset,
endss[i],
stride
);
}
this.endGeometry(feature);
}
@@ -210,5 +254,4 @@ class CanvasPolygonBuilder extends CanvasBuilder {
}
}
export default CanvasPolygonBuilder;

View File

@@ -1,15 +1,29 @@
/**
* @module ol/render/canvas/TextBuilder
*/
import {getUid} from '../../util.js';
import CanvasBuilder from './Builder.js';
import CanvasInstruction from './Instruction.js';
import GeometryType from '../../geom/GeometryType.js';
import TextPlacement from '../../style/TextPlacement.js';
import {asColorLike} from '../../colorlike.js';
import {
defaultFillStyle,
defaultFont,
defaultLineCap,
defaultLineDash,
defaultLineDashOffset,
defaultLineJoin,
defaultLineWidth,
defaultMiterLimit,
defaultPadding,
defaultStrokeStyle,
defaultTextAlign,
defaultTextBaseline,
registerFont,
} from '../canvas.js';
import {getUid} from '../../util.js';
import {intersects} from '../../extent.js';
import {matchingChunk} from '../../geom/flat/straightchunk.js';
import GeometryType from '../../geom/GeometryType.js';
import {defaultTextAlign, defaultPadding, defaultLineCap, defaultLineDashOffset, defaultLineDash, defaultLineJoin, defaultFillStyle, registerFont, defaultFont, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline} from '../canvas.js';
import CanvasInstruction from './Instruction.js';
import CanvasBuilder from './Builder.js';
import TextPlacement from '../../style/TextPlacement.js';
/**
* @const
* @enum {number}
@@ -25,10 +39,9 @@ export const TEXT_ALIGN = {
'hanging': 0.2,
'alphabetic': 0.8,
'ideographic': 0.8,
'bottom': 1
'bottom': 1,
};
class CanvasTextBuilder extends CanvasBuilder {
/**
* @param {number} tolerance Tolerance.
@@ -176,7 +189,9 @@ class CanvasTextBuilder extends CanvasBuilder {
} else if (geometryType == GeometryType.MULTI_LINE_STRING) {
ends = /** @type {import("../../geom/MultiLineString.js").default} */ (geometry).getEnds();
} else if (geometryType == GeometryType.POLYGON) {
ends = /** @type {import("../../geom/Polygon.js").default} */ (geometry).getEnds().slice(0, 1);
ends = /** @type {import("../../geom/Polygon.js").default} */ (geometry)
.getEnds()
.slice(0, 1);
} else if (geometryType == GeometryType.MULTI_POLYGON) {
const endss = /** @type {import("../../geom/MultiPolygon.js").default} */ (geometry).getEndss();
ends = [];
@@ -190,7 +205,13 @@ class CanvasTextBuilder extends CanvasBuilder {
let flatEnd;
for (let o = 0, oo = ends.length; o < oo; ++o) {
if (textAlign == undefined) {
const range = matchingChunk(textState.maxAngle, flatCoordinates, flatOffset, ends[o], stride);
const range = matchingChunk(
textState.maxAngle,
flatCoordinates,
flatOffset,
ends[o],
stride
);
flatOffset = range[0];
flatEnd = range[1];
} else {
@@ -201,16 +222,16 @@ class CanvasTextBuilder extends CanvasBuilder {
}
end = this.coordinates.length;
flatOffset = ends[o];
const declutterGroup = this.declutterGroups_ ?
(o === 0 ? this.declutterGroups_[0] : [].concat(this.declutterGroups_[0])) :
null;
const declutterGroup = this.declutterGroups_
? o === 0
? this.declutterGroups_[0]
: [].concat(this.declutterGroups_[0])
: null;
this.drawChars_(begin, end, declutterGroup);
begin = end;
}
this.endGeometry(feature);
} else {
let geometryWidths = null;
if (!textState.overflow) {
geometryWidths = [];
@@ -254,12 +275,22 @@ class CanvasTextBuilder extends CanvasBuilder {
break;
default:
}
end = this.appendFlatCoordinates(flatCoordinates, 0, end, stride, false, false);
end = this.appendFlatCoordinates(
flatCoordinates,
0,
end,
stride,
false,
false
);
this.saveTextStates_();
if (textState.backgroundFill || textState.backgroundStroke) {
this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke);
this.setFillStrokeStyle(
textState.backgroundFill,
textState.backgroundStroke
);
if (textState.backgroundFill) {
this.updateFillStyle(this.state, this.createFill);
this.hitDetectionInstructions.push(this.createFill(this.state));
@@ -276,24 +307,63 @@ class CanvasTextBuilder extends CanvasBuilder {
// For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at
// render time.
const pixelRatio = this.pixelRatio;
this.instructions.push([CanvasInstruction.DRAW_IMAGE, begin, end,
null, NaN, NaN, this.declutterGroups_, NaN, 1, 0, 0,
this.textRotateWithView_, this.textRotation_, 1, NaN,
textState.padding == defaultPadding ?
defaultPadding : textState.padding.map(function(p) {
return p * pixelRatio;
}),
!!textState.backgroundFill, !!textState.backgroundStroke,
this.text_, this.textKey_, this.strokeKey_, this.fillKey_,
this.textOffsetX_, this.textOffsetY_, geometryWidths
this.instructions.push([
CanvasInstruction.DRAW_IMAGE,
begin,
end,
null,
NaN,
NaN,
this.declutterGroups_,
NaN,
1,
0,
0,
this.textRotateWithView_,
this.textRotation_,
1,
NaN,
textState.padding == defaultPadding
? defaultPadding
: textState.padding.map(function (p) {
return p * pixelRatio;
}),
!!textState.backgroundFill,
!!textState.backgroundStroke,
this.text_,
this.textKey_,
this.strokeKey_,
this.fillKey_,
this.textOffsetX_,
this.textOffsetY_,
geometryWidths,
]);
this.hitDetectionInstructions.push([CanvasInstruction.DRAW_IMAGE, begin, end,
null, NaN, NaN, this.declutterGroups_, NaN, 1, 0, 0,
this.textRotateWithView_, this.textRotation_, 1 / this.pixelRatio, NaN,
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE,
begin,
end,
null,
NaN,
NaN,
this.declutterGroups_,
NaN,
1,
0,
0,
this.textRotateWithView_,
this.textRotation_,
1 / this.pixelRatio,
NaN,
textState.padding,
!!textState.backgroundFill, !!textState.backgroundStroke,
this.text_, this.textKey_, this.strokeKey_, this.fillKey_,
this.textOffsetX_, this.textOffsetY_, geometryWidths
!!textState.backgroundFill,
!!textState.backgroundStroke,
this.text_,
this.textKey_,
this.strokeKey_,
this.fillKey_,
this.textOffsetX_,
this.textOffsetY_,
geometryWidths,
]);
this.endGeometry(feature);
@@ -318,7 +388,7 @@ class CanvasTextBuilder extends CanvasBuilder {
lineWidth: strokeState.lineWidth,
lineJoin: strokeState.lineJoin,
miterLimit: strokeState.miterLimit,
lineDash: strokeState.lineDash
lineDash: strokeState.lineDash,
};
}
}
@@ -328,14 +398,14 @@ class CanvasTextBuilder extends CanvasBuilder {
font: textState.font,
textAlign: textState.textAlign || defaultTextAlign,
textBaseline: textState.textBaseline || defaultTextBaseline,
scale: textState.scale
scale: textState.scale,
};
}
const fillKey = this.fillKey_;
if (fillState) {
if (!(fillKey in this.fillStates)) {
this.fillStates[fillKey] = {
fillStyle: fillState.fillStyle
fillStyle: fillState.fillStyle,
};
}
}
@@ -356,26 +426,49 @@ class CanvasTextBuilder extends CanvasBuilder {
const fillKey = this.fillKey_;
this.saveTextStates_();
const pixelRatio = this.pixelRatio;
const baseline = TEXT_ALIGN[textState.textBaseline];
const offsetY = this.textOffsetY_ * pixelRatio;
const text = this.text_;
const textScale = textState.scale;
const strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0;
const strokeWidth = strokeState
? (strokeState.lineWidth * textScale) / 2
: 0;
this.instructions.push([CanvasInstruction.DRAW_CHARS,
begin, end, baseline, declutterGroup,
textState.overflow, fillKey, textState.maxAngle,
this.instructions.push([
CanvasInstruction.DRAW_CHARS,
begin,
end,
baseline,
declutterGroup,
textState.overflow,
fillKey,
textState.maxAngle,
pixelRatio,
offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1
]);
this.hitDetectionInstructions.push([CanvasInstruction.DRAW_CHARS,
begin, end, baseline, declutterGroup,
textState.overflow, fillKey, textState.maxAngle,
offsetY,
strokeKey,
strokeWidth * pixelRatio,
text,
textKey,
1,
offsetY, strokeKey, strokeWidth, text, textKey, 1 / pixelRatio
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_CHARS,
begin,
end,
baseline,
declutterGroup,
textState.overflow,
fillKey,
textState.maxAngle,
1,
offsetY,
strokeKey,
strokeWidth,
text,
textKey,
1 / pixelRatio,
]);
}
@@ -401,7 +494,8 @@ class CanvasTextBuilder extends CanvasBuilder {
this.textFillState_ = fillState;
}
fillState.fillStyle = asColorLike(
textFillStyle.getColor() || defaultFillStyle);
textFillStyle.getColor() || defaultFillStyle
);
}
const textStrokeStyle = textStyle.getStroke();
@@ -421,14 +515,15 @@ class CanvasTextBuilder extends CanvasBuilder {
strokeState.lineCap = textStrokeStyle.getLineCap() || defaultLineCap;
strokeState.lineDash = lineDash ? lineDash.slice() : defaultLineDash;
strokeState.lineDashOffset =
lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset;
lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset;
strokeState.lineJoin = textStrokeStyle.getLineJoin() || defaultLineJoin;
strokeState.lineWidth =
lineWidth === undefined ? defaultLineWidth : lineWidth;
lineWidth === undefined ? defaultLineWidth : lineWidth;
strokeState.miterLimit =
miterLimit === undefined ? defaultMiterLimit : miterLimit;
miterLimit === undefined ? defaultMiterLimit : miterLimit;
strokeState.strokeStyle = asColorLike(
textStrokeStyle.getColor() || defaultStrokeStyle);
textStrokeStyle.getColor() || defaultStrokeStyle
);
}
textState = this.textState_;
@@ -440,7 +535,8 @@ class CanvasTextBuilder extends CanvasBuilder {
textState.maxAngle = textStyle.getMaxAngle();
textState.placement = textStyle.getPlacement();
textState.textAlign = textStyle.getTextAlign();
textState.textBaseline = textStyle.getTextBaseline() || defaultTextBaseline;
textState.textBaseline =
textStyle.getTextBaseline() || defaultTextBaseline;
textState.backgroundFill = textStyle.getBackgroundFill();
textState.backgroundStroke = textStyle.getBackgroundStroke();
textState.padding = textStyle.getPadding() || defaultPadding;
@@ -453,21 +549,36 @@ class CanvasTextBuilder extends CanvasBuilder {
this.text_ = textStyle.getText() || '';
this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX;
this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY;
this.textRotateWithView_ = textRotateWithView === undefined ? false : textRotateWithView;
this.textRotateWithView_ =
textRotateWithView === undefined ? false : textRotateWithView;
this.textRotation_ = textRotation === undefined ? 0 : textRotation;
this.strokeKey_ = strokeState ?
(typeof strokeState.strokeStyle == 'string' ? strokeState.strokeStyle : getUid(strokeState.strokeStyle)) +
strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth +
strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' :
'';
this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?') + (textState.textBaseline || '?');
this.fillKey_ = fillState ?
(typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) :
'';
this.strokeKey_ = strokeState
? (typeof strokeState.strokeStyle == 'string'
? strokeState.strokeStyle
: getUid(strokeState.strokeStyle)) +
strokeState.lineCap +
strokeState.lineDashOffset +
'|' +
strokeState.lineWidth +
strokeState.lineJoin +
strokeState.miterLimit +
'[' +
strokeState.lineDash.join() +
']'
: '';
this.textKey_ =
textState.font +
textState.scale +
(textState.textAlign || '?') +
(textState.textBaseline || '?');
this.fillKey_ = fillState
? typeof fillState.fillStyle == 'string'
? fillState.fillStyle
: '|' + getUid(fillState.fillStyle)
: '';
}
}
}
export default CanvasTextBuilder;

View File

@@ -3,10 +3,10 @@
*/
import CanvasImmediateRenderer from './Immediate.js';
import {createCanvasContext2D} from '../../dom.js';
import {Icon} from '../../style.js';
import IconAnchorUnits from '../../style/IconAnchorUnits.js';
import GeometryType from '../../geom/GeometryType.js';
import IconAnchorUnits from '../../style/IconAnchorUnits.js';
import {Icon} from '../../style.js';
import {createCanvasContext2D} from '../../dom.js';
import {intersects} from '../../extent.js';
import {numberSafeCompareFunction} from '../../array.js';
@@ -24,13 +24,27 @@ import {numberSafeCompareFunction} from '../../array.js';
* @param {number} rotation Rotation.
* @return {ImageData} Hit detection image data.
*/
export function createHitDetectionImageData(size, transforms, features, styleFunction, extent, resolution, rotation) {
export function createHitDetectionImageData(
size,
transforms,
features,
styleFunction,
extent,
resolution,
rotation
) {
const width = size[0] / 2;
const height = size[1] / 2;
const context = createCanvasContext2D(width, height);
context.imageSmoothingEnabled = false;
const canvas = context.canvas;
const renderer = new CanvasImmediateRenderer(context, 0.5, extent, null, rotation);
const renderer = new CanvasImmediateRenderer(
context,
0.5,
extent,
null,
rotation
);
const featureCount = features.length;
// Stretch hit detection index to use the whole available color range
const indexFactor = Math.floor((256 * 256 * 256 - 1) / featureCount);
@@ -80,19 +94,21 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
const height = imgSize ? imgSize[1] : img.height;
const iconContext = createCanvasContext2D(width, height);
iconContext.drawImage(img, 0, 0);
style.setImage(new Icon({
img: img,
imgSize: imgSize,
anchor: image.getAnchor(),
anchorXUnits: IconAnchorUnits.PIXELS,
anchorYUnits: IconAnchorUnits.PIXELS,
offset: image.getOrigin(),
size: image.getSize(),
opacity: image.getOpacity(),
scale: image.getScale(),
rotation: image.getRotation(),
rotateWithView: image.getRotateWithView()
}));
style.setImage(
new Icon({
img: img,
imgSize: imgSize,
anchor: image.getAnchor(),
anchorXUnits: IconAnchorUnits.PIXELS,
anchorYUnits: IconAnchorUnits.PIXELS,
offset: image.getOrigin(),
size: image.getSize(),
opacity: image.getOpacity(),
scale: image.getScale(),
rotation: image.getRotation(),
rotateWithView: image.getRotateWithView(),
})
);
}
const zIndex = Number(style.getZIndex());
let byGeometryType = featuresByZIndex[zIndex];
@@ -106,12 +122,17 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
}
const geometry = style.getGeometryFunction()(feature);
if (geometry && intersects(extent, geometry.getExtent())) {
byGeometryType[geometry.getType().replace('Multi', '')].push(geometry, style);
byGeometryType[geometry.getType().replace('Multi', '')].push(
geometry,
style
);
}
}
}
const zIndexKeys = Object.keys(featuresByZIndex).map(Number).sort(numberSafeCompareFunction);
const zIndexKeys = Object.keys(featuresByZIndex)
.map(Number)
.sort(numberSafeCompareFunction);
for (let i = 0, ii = zIndexKeys.length; i < ii; ++i) {
const byGeometryType = featuresByZIndex[zIndexKeys[i]];
for (const type in byGeometryType) {
@@ -140,11 +161,13 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
export function hitDetect(pixel, features, imageData) {
const resultFeatures = [];
if (imageData) {
const index = (Math.round(pixel[0] / 2) + Math.round(pixel[1] / 2) * imageData.width) * 4;
const index =
(Math.round(pixel[0] / 2) + Math.round(pixel[1] / 2) * imageData.width) *
4;
const r = imageData.data[index];
const g = imageData.data[index + 1];
const b = imageData.data[index + 2];
const i = b + (256 * (g + (256 * r)));
const i = b + 256 * (g + 256 * r);
const indexFactor = Math.floor((256 * 256 * 256 - 1) / features.length);
if (i && i % indexFactor === 0) {
resultFeatures.push(features[i / indexFactor - 1]);