Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapbox expressions #2

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
bezier:
dependency: transitive
description:
name: bezier
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
boolean_selector:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion lib/src/constants.dart
Original file line number Diff line number Diff line change
@@ -1 +1 @@
final int tileSize = 256;
const int tileSize = 256;
32 changes: 32 additions & 0 deletions lib/src/expressions/argument_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:vector_tile/vector_tile_value.dart';

import 'expression.dart';

class ArgumentExpression<T> extends Expression<T> {
final String key;
ArgumentExpression(this.key);

@override
T? evaluate(Map<String, dynamic> args) {
final value = args[key];

if (value is T?) return value;

if (T == double && value is num) {
return value.toDouble() as T;
}

if (value is VectorTileValue) {
switch (T) {
case double:
return value.doubleValue as T?;
case bool:
return value.boolValue as T?;
case String:
return value.stringValue as T?;
}
}

return null;
}
}
27 changes: 27 additions & 0 deletions lib/src/expressions/case_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'expression.dart';

class Case<T> {
final Expression<bool> condition;
final Expression<T>? value;

Case(this.condition, this.value);
}

class CaseExpression<T> extends Expression<T> {
final Iterable<Case<T>> _cases;
final Expression<T>? _fallback;

CaseExpression(this._cases, this._fallback);

@override
T? evaluate(Map<String, dynamic> args) {
for (final $case in _cases) {
final boolExpression = $case.condition.evaluate(args) ?? false;
if (boolExpression) {
return $case.value?.evaluate(args);
}
}

return _fallback?.evaluate(args);
}
}
17 changes: 17 additions & 0 deletions lib/src/expressions/coalesce_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'expression.dart';

class CoalesceExpression<T> extends Expression<T> {
final Iterable<Expression<T>> _delegates;

CoalesceExpression(this._delegates);

@override
T? evaluate(Map<String, dynamic> args) {
for (final delegate in _delegates) {
final result = delegate.evaluate(args);
if (result != null) return result;
}

return null;
}
}
3 changes: 3 additions & 0 deletions lib/src/expressions/expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
abstract class Expression<T> {
T? evaluate(Map<String, dynamic> args);
}
12 changes: 12 additions & 0 deletions lib/src/expressions/function_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'expression.dart';

// This expression mostly exists to make the refactoring to Expressions easier.
// It's probably desirable to work towards removing this eventually.
class FunctionExpression<T> extends Expression<T> {
final T? Function(Map<String, dynamic> args) _evaluate;

FunctionExpression(this._evaluate);

@override
T? evaluate(Map<String, dynamic> args) => _evaluate(args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:bezier/bezier.dart';
import 'package:vector_math/vector_math.dart';
mvarendorff marked this conversation as resolved.
Show resolved Hide resolved
import 'package:vector_tile_renderer/src/themes/theme_function_model.dart';

import '../expression.dart';
import 'interpolation_expression.dart';

class CubicBezierInterpolationExpression<T> extends InterpolationExpression<T> {
final Vector2 _c1;
final Vector2 _c2;

CubicBezierInterpolationExpression(
this._c1,
this._c2,
Expression<double> input,
List<FunctionStop<T>> stops,
) : super(input, stops);

@override
double getInterpolationFactor(
double input, double lowerValue, double upperValue) {
final t = exponentialInterpolation(input, 1, lowerValue, upperValue);
final curve = CubicBezier([Vector2(0, 0), _c1, _c2, Vector2(1, 1)]);
return curve.pointAt(t).y;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:vector_tile_renderer/src/themes/theme_function_model.dart';

import '../expression.dart';
import 'interpolation_expression.dart';

class ExponentialInterpolationExpression<T> extends InterpolationExpression<T> {
final double _base;
ExponentialInterpolationExpression(
this._base, Expression<double> input, List<FunctionStop<T>> stops)
: super(input, stops);

@override
double getInterpolationFactor(
double input, double lowerValue, double upperValue) =>
exponentialInterpolation(input, _base, lowerValue, upperValue);
}
87 changes: 87 additions & 0 deletions lib/src/expressions/interpolate/interpolation_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:vector_tile_renderer/src/themes/theme_function_model.dart';

import '../expression.dart';

abstract class InterpolationExpression<T> extends Expression<T> {
final Expression<double> input;
final List<FunctionStop<T>> stops;

InterpolationExpression(this.input, this.stops);

double getInterpolationFactor(
double input,
double lowerValue,
double upperValue,
);

exponentialInterpolation(
double input, double base, double lowerValue, double upperValue) {
final difference = upperValue - lowerValue;
final progress = input - lowerValue;
if (difference == 0) {
return 0;
} else if (base == 1) {
return progress / difference;
} else {
return (pow(base, progress) - 1) / (pow(base, difference) - 1);
}
}

double? _interpolateDouble(double begin, double end, double t) {
final diff = end - begin;
return begin + diff * t;
}

Color? _interpolateColor(Color? begin, Color? end, double t) {
final tween = ColorTween(begin: begin, end: end);
return tween.transform(t);
}

@override
T? evaluate(Map<String, dynamic> args) {
final functionInput = input.evaluate(args)!;

final firstStop = stops.first;
if (functionInput <= firstStop.zoom.evaluate(args)!)
return firstStop.value.evaluate(args);

final lastStop = stops.last;
if (functionInput > lastStop.zoom.evaluate(args)!)
return lastStop.value.evaluate(args);

final firstSmallerStopIndex = stops.lastIndexWhere(
(stop) => stop.zoom.evaluate(args)! < functionInput,
);
final index = max(0, firstSmallerStopIndex);
final smallerStop = stops[index];
final largerStop = stops[index + 1];

final smallerZoom = smallerStop.zoom.evaluate(args)!;
final largerZoom = largerStop.zoom.evaluate(args)!;

final smallerValue = smallerStop.value.evaluate(args)!;
final largerValue = largerStop.value.evaluate(args)!;

final t = getInterpolationFactor(functionInput, smallerZoom, largerZoom);

if (T == double) {
return _interpolateDouble(
smallerValue as double,
largerValue as double,
t,
) as T?;
}

if (T == Color) {
return _interpolateColor(
smallerValue as Color?,
largerValue as Color?,
t,
) as T?;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:vector_tile_renderer/src/themes/theme_function_model.dart';

import '../expression.dart';
import 'interpolation_expression.dart';

class LinearInterpolationExpression<T> extends InterpolationExpression<T> {
LinearInterpolationExpression(
Expression<double> input, List<FunctionStop<T>> stops)
: super(input, stops);

@override
double getInterpolationFactor(
double input, double lowerValue, double upperValue) =>
exponentialInterpolation(input, 1, lowerValue, upperValue);
}
35 changes: 35 additions & 0 deletions lib/src/expressions/match_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:vector_tile_renderer/src/expressions/expression.dart';

class Match<Input, Output> {
final dynamic input;
final Expression<Output>? output;

Match(this.input, this.output)
: assert(input is List<Input> || input is Input);
}

class MatchExpression<Output, Input> extends Expression<Output> {
final Expression<Input>? _compare;
final Iterable<Match<Input, Output>> _matches;
final Expression<Output>? _fallback;

MatchExpression(this._compare, this._matches, this._fallback);

@override
Output? evaluate(Map<String, dynamic> args) {
final compare = _compare?.evaluate(args);
if (compare == null) {
return _fallback?.evaluate(args);
}

for (final match in _matches) {
final input = match.input;
if (input == compare ||
(input is List && match.input.contains(compare))) {
return match.output?.evaluate(args);
}
}

return _fallback?.evaluate(args);
}
}
32 changes: 32 additions & 0 deletions lib/src/expressions/step_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'expression.dart';

class Step<T> {
final num step;
final Expression<T>? value;

Step(this.step, this.value);
}

class StepExpression<T> extends Expression<T> {
final Expression<double> _input;
final Expression<T> _base;
final Iterable<Step<T>> _steps;

StepExpression(this._input, this._base, this._steps)
: assert(_steps.isNotEmpty);

@override
T? evaluate(Map<String, dynamic> args) {
final input = _input.evaluate(args)!;

if (input < _steps.first.step) {
return _base.evaluate(args);
}

final lastLessThanStop = _steps.lastWhere(
(stop) => stop.step < input,
);

return lastLessThanStop.value?.evaluate(args);
}
}
45 changes: 45 additions & 0 deletions lib/src/expressions/text_halo_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'dart:ui';

import 'package:vector_tile_renderer/src/expressions/expression.dart';

class TextHaloExpression extends Expression<List<Shadow>> {
final Expression<Color> _color;
final double _haloWidth;

TextHaloExpression(this._color, this._haloWidth);

List<Shadow>? evaluate(Map<String, dynamic> args) {
final color = _color.evaluate(args);
if (color == null) {
return null;
}

final zoom = args['zoom'];

double offset = _haloWidth / zoom;
double radius = _haloWidth;

return [
Shadow(
offset: Offset(-offset, -offset),
blurRadius: radius,
color: color,
),
Shadow(
offset: Offset(offset, offset),
blurRadius: radius,
color: color,
),
Shadow(
offset: Offset(offset, -offset),
blurRadius: radius,
color: color,
),
Shadow(
offset: Offset(-offset, offset),
blurRadius: radius,
color: color,
),
];
}
}
8 changes: 8 additions & 0 deletions lib/src/expressions/value_expression.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'expression.dart';

class ValueExpression<T> extends Expression<T> {
final T? _value;
ValueExpression(this._value);

T? evaluate(Map<String, dynamic> values) => _value;
}