Skip to content

Commit

Permalink
refactor(style): change Style.drawingFromContext(ctx) to Style.getFro…
Browse files Browse the repository at this point in the history
…mContext(ctx) for hierarchization of style properties
  • Loading branch information
ftoromanoff committed Dec 13, 2023
1 parent 6b44ef9 commit 17bbe88
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 76 deletions.
15 changes: 10 additions & 5 deletions src/Converter/Feature2Mesh.js
Expand Up @@ -7,7 +7,7 @@ import Extent from 'Core/Geographic/Extent';
import Crs from 'Core/Geographic/Crs';
import OrientationUtils from 'Utils/OrientationUtils';
import Coordinates from 'Core/Geographic/Coordinates';
import { StyleContext } from 'Core/Style';
import Style, { StyleContext } from 'Core/Style';

const coord = new Coordinates('EPSG:4326', 0, 0, 0);
const context = new StyleContext();
Expand Down Expand Up @@ -194,6 +194,7 @@ function featureToPoint(feature, options) {

const pointMaterialSize = [];
context.globals = { point: true };
context.setFeature(feature);

for (const geometry of feature.geometries) {
const start = geometry.indices[0].offset;
Expand All @@ -208,7 +209,7 @@ function featureToPoint(feature, options) {
}

coord.copy(context.setLocalCoordinatesFromArray(feature.vertices, v));
const style = feature.style.applyContext(context);
const style = Style.applyContext(context);
const { base_altitude, color, radius } = style.point;
coord.z = 0;

Expand Down Expand Up @@ -253,6 +254,7 @@ function featureToLine(feature, options) {

const lineMaterialWidth = [];
context.globals = { stroke: true };
context.setFeature(feature);

const countIndices = (count - feature.geometries.length) * 2;
const indices = getIntArrayFromSize(countIndices, count);
Expand Down Expand Up @@ -288,7 +290,7 @@ function featureToLine(feature, options) {
}

coord.copy(context.setLocalCoordinatesFromArray(feature.vertices, v));
const style = feature.style.applyContext(context);
const style = Style.applyContext(context);
const { base_altitude, color, width } = style.stroke;
coord.z = 0;

Expand Down Expand Up @@ -322,6 +324,7 @@ function featureToPolygon(feature, options) {
const batchIds = new Uint32Array(vertices.length / 3);
const batchId = options.batchId || ((p, id) => id);
context.globals = { fill: true };
context.setFeature(feature);

inverseScale.setFromMatrixScale(context.collection.matrixWorldInverse);
normal.set(0, 0, 1).multiply(inverseScale);
Expand Down Expand Up @@ -349,7 +352,7 @@ function featureToPolygon(feature, options) {
}

coord.copy(context.setLocalCoordinatesFromArray(feature.vertices, i));
const style = feature.style.applyContext(context);
const style = Style.applyContext(context);
const { base_altitude, color } = style.fill;
coord.z = 0;

Expand Down Expand Up @@ -410,6 +413,7 @@ function featureToExtrudedPolygon(feature, options) {
let featureId = 0;

context.globals = { fill: true };
context.setFeature(feature);
inverseScale.setFromMatrixScale(context.collection.matrixWorldInverse);
normal.set(0, 0, 1).multiply(inverseScale);
coord.setCrs(context.collection.crs);
Expand All @@ -435,7 +439,7 @@ function featureToExtrudedPolygon(feature, options) {

coord.copy(context.setLocalCoordinatesFromArray(ptsIn, i));

const style = feature.style.applyContext(context);
const style = Style.applyContext(context);
const { base_altitude, extrusion_height, color } = style.fill;
coord.z = 0;

Expand Down Expand Up @@ -653,6 +657,7 @@ export default {
// as in examples/source_file_gpx_3d.html.
options.layer = this || { style: options.style };
}
context.layerStyle = options.layer.style;

context.setCollection(collection);

Expand Down
20 changes: 12 additions & 8 deletions src/Converter/Feature2Texture.js
Expand Up @@ -25,7 +25,6 @@ function drawPolygon(ctx, vertices, indices = [{ offset: 0, count: 1 }], style =
if (vertices.length === 0) {
return;
}

if (style.length) {
for (const s of style) {
_drawPolygon(ctx, vertices, indices, s, size, extent, invCtxScale, canBeFilled);
Expand Down Expand Up @@ -73,19 +72,21 @@ function drawPoint(ctx, x, y, style = {}, invCtxScale) {

const coord = new Coordinates('EPSG:4326', 0, 0, 0);

function drawFeature(ctx, feature, extent, style, invCtxScale) {
function drawFeature(ctx, feature, extent, invCtxScale) {
const extentDim = extent.planarDimensions();
const scaleRadius = extentDim.x / ctx.canvas.width;

context.setFeature(feature);

for (const geometry of feature.geometries) {
if (Extent.intersectsExtent(geometry.extent, extent)) {
context.setGeometry(geometry);
const contextStyle = (geometry.properties.style || style).applyContext(context);

const contextStyle = Style.applyContext(context);

if (contextStyle) {
if (
feature.type === FEATURE_TYPES.POINT
&& contextStyle.point
feature.type === FEATURE_TYPES.POINT && contextStyle.point
) {
// cross multiplication to know in the extent system the real size of
// the point
Expand Down Expand Up @@ -121,8 +122,9 @@ const featureExtent = new Extent('EPSG:4326', 0, 0, 0, 0);
export default {
// backgroundColor is a THREE.Color to specify a color to fill the texture
// with, given there is no feature passed in parameter
createTextureFromFeature(collection, extent, sizeTexture, style = {}, backgroundColor) {
createTextureFromFeature(collection, extent, sizeTexture, layerStyle = {}, backgroundColor) {
let texture;
context.layerStyle = layerStyle;

if (collection) {
// A texture is instancied drawn canvas
Expand All @@ -139,7 +141,9 @@ export default {
ctx.fillStyle = backgroundColor.getStyle();
ctx.fillRect(0, 0, sizeTexture, sizeTexture);
}
ctx.globalCompositeOperation = style.globalCompositeOperation || 'source-over';

// Documentation needed !!
ctx.globalCompositeOperation = layerStyle.globalCompositeOperation || 'source-over';
ctx.imageSmoothingEnabled = false;
ctx.lineJoin = 'round';

Expand Down Expand Up @@ -177,7 +181,7 @@ export default {

// Draw the canvas
for (const feature of collection.features) {
drawFeature(ctx, feature, featureExtent, feature.style || style, invCtxScale);
drawFeature(ctx, feature, featureExtent, invCtxScale);
}

texture = new THREE.CanvasTexture(c);
Expand Down
103 changes: 69 additions & 34 deletions src/Core/Style.js
Expand Up @@ -166,6 +166,7 @@ function defineStyleProperty(style, category, name, value, defaultValue) {
* @property {Object} collection The FeatureCollection to which the FeatureGeometry is attached.
* @property {Object} properties Properties of the FeatureGeometry.
* @property {string} type Geometry type of the feature. Can be `point`, `line`, or `polygon`.
* @property {Style} style Style of the FeatureGeometry computed from Layer.style and user.style.
* @property {Coordinates} coordinates The coordinates (in world space) of the last vertex (x, y, z) set with
* setLocalCoordinatesFromArray().
* private properties:
Expand Down Expand Up @@ -215,6 +216,38 @@ export class StyleContext {
return this.#feature.type;
}

get style() {
const layerStyle = this.layerStyle || {};
let featureStyle = this.#feature.style;
if (featureStyle instanceof Function) {
featureStyle = readExpression(featureStyle, this);
}
const style = {
fill: {
...featureStyle.fill,
...layerStyle.fill,
},
stroke: {
...featureStyle.stroke,
...layerStyle.stroke,
},
point: {
...featureStyle.point,
...layerStyle.point,
},
icon: {
...featureStyle.icon,
...layerStyle.icon,
},
text: {
...featureStyle.text,
...layerStyle.text,
},
order: layerStyle.order || featureStyle.order,
};
return style;
}

get coordinates() {
if (!this.#worldCoordsComputed) {
this.#worldCoordsComputed = true;
Expand Down Expand Up @@ -645,10 +678,10 @@ class Style {
}

/**
* Clones this style.
*
* @return {Style} The new style, cloned from this one.
*/
* Clones this style.
*
* @return {Style} The new style, cloned from this one.
*/
clone() {
const clone = new Style();
return clone.copy(this);
Expand All @@ -662,27 +695,47 @@ class Style {
*
* @return {Style} mapped style depending on context.
*/
applyContext(context) {
static applyContext(context) {
const styleConc = new Style(context.style);
const style = {};
if (this.fill.color || this.fill.pattern || context.globals.fill) {
mapPropertiesFromContext('fill', this, style, context);
if (styleConc.fill.color || styleConc.fill.pattern || context.globals.fill) {
mapPropertiesFromContext('fill', styleConc, style, context);
}
if (this.stroke.color || context.globals.stroke) {
mapPropertiesFromContext('stroke', this, style, context);
if (styleConc.stroke.color || context.globals.stroke) {
mapPropertiesFromContext('stroke', styleConc, style, context);
}
if (this.point.color || this.point.model || context.globals.point) {
mapPropertiesFromContext('point', this, style, context);
if (styleConc.point.color || styleConc.point.model || context.globals.point) {
mapPropertiesFromContext('point', styleConc, style, context);
}
if (this.text || context.globals.text) {
mapPropertiesFromContext('text', this, style, context);

if (styleConc.text || context.globals.text) {
mapPropertiesFromContext('text', styleConc, style, context);
}
if (this.icon || context.globals.icon) {
mapPropertiesFromContext('icon', this, style, context);
if (styleConc.icon || context.globals.icon) {
mapPropertiesFromContext('icon', styleConc, style, context);
}
style.order = this.order;
style.order = styleConc.order;
return new Style(style);
}

/**
* Returns a string, associating `style.text.field` and properties to use to
* replace the keys in `style.text.field`.
*
* @param {FeatureContext} context The context linked to the feature
*
* @return {string|undefined} The formatted string if `style.text.field` is defined, nothing otherwise.
*/
getTextFromProperties(context) {
if (!this.text.field) { return; }

if (this.text.field.expression) {
return readExpression(this.text.field, context);
} else {
return this.text.field.replace(/\{(.+?)\}/g, (a, b) => (context.properties()[b] || '')).trim();
}
}

/**
* set Style from (geojson-like) properties.
* @param {Object} properties (geojson-like) properties.
Expand Down Expand Up @@ -1055,24 +1108,6 @@ class Style {
return this.text.anchor;
}
}

/**
* Returns a string, associating `style.text.field` and properties to use to
* replace the keys in `style.text.field`.
*
* @param {Object} ctx - An object containing the feature context.
*
* @return {String|undefined} The formatted string if `style.text.field` is defined, nothing otherwise.
*/
getTextFromProperties(ctx) {
if (!this.text.field) { return; }

if (this.text.field.expression) {
return readExpression(this.text.field, ctx);
} else {
return this.text.field.replace(/\{(.+?)\}/g, (a, b) => (ctx.properties[b] || '')).trim();
}
}
}

// Add custom style sheet with iTowns specifics
Expand Down
26 changes: 14 additions & 12 deletions src/Layer/LabelLayer.js
Expand Up @@ -6,7 +6,7 @@ import Coordinates from 'Core/Geographic/Coordinates';
import Extent from 'Core/Geographic/Extent';
import Label from 'Core/Label';
import { FEATURE_TYPES } from 'Core/Feature';
import { readExpression, StyleContext } from 'Core/Style';
import Style, { readExpression, StyleContext } from 'Core/Style';
import { ScreenGrid } from 'Renderer/Label2DRenderer';

const context = new StyleContext();
Expand Down Expand Up @@ -249,17 +249,19 @@ class LabelLayer extends GeometryLayer {
text: true,
zoom: extent.zoom,
};
context.layerStyle = this.style;

data.features.forEach((f) => {
// TODO: add support for LINE and POLYGON
if (f.type !== FEATURE_TYPES.POINT) {
return;
}
context.setFeature(f);

const featureField = f.style.text.field;
const featureField = f.style?.text?.field;

// determine if altitude style is specified by the user
const altitudeStyle = f.style.point.base_altitude;
const altitudeStyle = f.style?.point?.base_altitude;
const isDefaultElevationStyle = altitudeStyle instanceof Function && altitudeStyle.name == 'baseAltitudeDefault';

// determine if the altitude needs update with ElevationLayer
Expand All @@ -273,31 +275,31 @@ class LabelLayer extends GeometryLayer {
coord.applyMatrix4(data.matrixWorld);

if (!_extent.isPointInside(coord)) { return; }

const geometryField = g.properties.style && g.properties.style.text.field;
const geometryField = g.properties.style && g.properties.style.text && g.properties.style.text.field;

context.setGeometry(g);
let content;
if (this.labelDomelement) {
content = readExpression(this.labelDomelement, context);
} else if (!geometryField && !featureField && !layerField) {
// Check if there is an icon, with no text
if (!(g.properties.style && (g.properties.style.icon.source || g.properties.style.icon.id))
&& !(f.style && (f.style.icon.source || f.style.icon.id))
&& !(this.style && (this.style.icon.source || this.style.icon.id))) {
if (!(g.properties.style && (g.properties.style.icon.source || g.properties.style.icon.key))
&& !(f.style && f.style.icon && (f.style.icon.source || f.style.icon.key))
&& !(this.style && this.style.icon && (this.style.icon.source || this.style.icon.key))) {
return;
}
} else if (geometryField) {
content = g.properties.style.getTextFromProperties(context);
content = new Style(g.properties.style).getTextFromProperties(context);
} else if (featureField) {
content = f.style.getTextFromProperties(context);
content = new Style(f.style).getTextFromProperties(context);
} else if (layerField) {
content = this.style.getTextFromProperties(context);
content = new Style(this.style).getTextFromProperties(context);
}

const style = (g.properties.style || f.style || this.style).applyContext(context);
const style = Style.applyContext(context);

const label = new Label(content, coord.clone(), style);

label.layerId = this.id;
label.padding = this.margin || label.padding;

Expand Down

0 comments on commit 17bbe88

Please sign in to comment.