flocc 0.5.21 → 0.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/environments/Environment.d.ts +38 -0
- package/dist/flocc.es.js +736 -107
- package/dist/flocc.js +736 -107
- package/dist/helpers/Rule.d.ts +56 -1
- package/dist/main.d.ts +1 -1
- package/dist/renderers/CanvasRenderer.d.ts +58 -0
- package/package.json +1 -1
- package/dist/environments/NewEnvironment.d.ts +0 -76
- package/dist/helpers/BS.d.ts +0 -6
- package/dist/types/instanceOfPoint.d.ts +0 -2
- package/dist/types/isAgent.d.ts +0 -2
- package/dist/utils/copyArray.d.ts +0 -10
- package/dist/utils/lcg.d.ts +0 -1
- package/dist/utils/randomizeOrder.d.ts +0 -12
package/dist/flocc.es.js
CHANGED
|
@@ -817,7 +817,37 @@ var Operators;
|
|
|
817
817
|
Operators["agent"] = "agent";
|
|
818
818
|
Operators["environment"] = "environment";
|
|
819
819
|
Operators["vector"] = "vector";
|
|
820
|
+
Operators["log"] = "log";
|
|
820
821
|
})(Operators || (Operators = {}));
|
|
822
|
+
var operatorInfo = {
|
|
823
|
+
add: { min: 2, max: 2 },
|
|
824
|
+
subtract: { min: 2, max: 2 },
|
|
825
|
+
multiply: { min: 2, max: 2 },
|
|
826
|
+
divide: { min: 2, max: 2 },
|
|
827
|
+
mod: { min: 2, max: 2 },
|
|
828
|
+
power: { min: 2, max: 2 },
|
|
829
|
+
get: { min: 1, max: 1 },
|
|
830
|
+
set: { min: 2, max: 2 },
|
|
831
|
+
enqueue: { min: 2, max: 2 },
|
|
832
|
+
local: { min: 1, max: 2 },
|
|
833
|
+
if: { min: 2, max: 3 },
|
|
834
|
+
and: { min: 2, max: 2 },
|
|
835
|
+
or: { min: 2, max: 2 },
|
|
836
|
+
gt: { min: 2, max: 2 },
|
|
837
|
+
gte: { min: 2, max: 2 },
|
|
838
|
+
lt: { min: 2, max: 2 },
|
|
839
|
+
lte: { min: 2, max: 2 },
|
|
840
|
+
eq: { min: 2, max: 2 },
|
|
841
|
+
map: { min: 2, max: 2 },
|
|
842
|
+
filter: { min: 2, max: 2 },
|
|
843
|
+
key: { min: 2, max: 2 },
|
|
844
|
+
method: { min: 2, max: Infinity },
|
|
845
|
+
agent: { min: 0, max: 0 },
|
|
846
|
+
environment: { min: 0, max: 0 },
|
|
847
|
+
vector: { min: 1, max: Infinity },
|
|
848
|
+
log: { min: 1, max: Infinity }
|
|
849
|
+
};
|
|
850
|
+
var ARITHMETIC_OPS = new Set(["add", "subtract", "multiply", "divide", "mod", "power"]);
|
|
821
851
|
var add = function (a, b) { return a + b; };
|
|
822
852
|
var subtract = function (a, b) { return a - b; };
|
|
823
853
|
var multiply = function (a, b) { return a * b; };
|
|
@@ -841,6 +871,127 @@ var method = function (obj, name) {
|
|
|
841
871
|
return null;
|
|
842
872
|
return obj[name].apply(obj, args);
|
|
843
873
|
};
|
|
874
|
+
/**
|
|
875
|
+
* Format a single value for step display. If isFirst is true and the value is
|
|
876
|
+
* a known operator string, render it bare (unquoted).
|
|
877
|
+
*/
|
|
878
|
+
function formatStepValue(val, isFirst) {
|
|
879
|
+
if (val === null || val === undefined)
|
|
880
|
+
return String(val);
|
|
881
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
882
|
+
return String(val);
|
|
883
|
+
if (typeof val === "string") {
|
|
884
|
+
if (isFirst && val in Operators)
|
|
885
|
+
return val;
|
|
886
|
+
return JSON.stringify(val);
|
|
887
|
+
}
|
|
888
|
+
if (val instanceof Array) {
|
|
889
|
+
if (val.length === 0)
|
|
890
|
+
return "[]";
|
|
891
|
+
var parts = val.map(function (s, i) { return formatStepValue(s, i === 0); });
|
|
892
|
+
return "(" + parts.join(" ") + ")";
|
|
893
|
+
}
|
|
894
|
+
if (typeof val === "object") {
|
|
895
|
+
try {
|
|
896
|
+
return JSON.stringify(val);
|
|
897
|
+
}
|
|
898
|
+
catch (_a) {
|
|
899
|
+
return String(val);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return String(val);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Format a step into a readable string representation for logging.
|
|
906
|
+
*/
|
|
907
|
+
function formatStep(step) {
|
|
908
|
+
return formatStepValue(step, false);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Format a value as an S-expression atom (no recursion into arrays).
|
|
912
|
+
*/
|
|
913
|
+
function formatAtom(val) {
|
|
914
|
+
if (val === null || val === undefined)
|
|
915
|
+
return "null";
|
|
916
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
917
|
+
return String(val);
|
|
918
|
+
if (typeof val === "string")
|
|
919
|
+
return JSON.stringify(val);
|
|
920
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
921
|
+
try {
|
|
922
|
+
return JSON.stringify(val);
|
|
923
|
+
}
|
|
924
|
+
catch (_a) {
|
|
925
|
+
return "[object]";
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return String(val);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Format a value for S-expression display. The first element (operator) of
|
|
932
|
+
* a step is rendered as a bare identifier; other string values are quoted.
|
|
933
|
+
*/
|
|
934
|
+
function formatSexpValue(val, isOperator) {
|
|
935
|
+
if (isOperator && typeof val === "string")
|
|
936
|
+
return val;
|
|
937
|
+
return formatAtom(val);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Pretty-print a single step as an S-expression with width-based wrapping.
|
|
941
|
+
*/
|
|
942
|
+
function prettyFormatStep(step, indentUnit, maxLineWidth, currentIndent) {
|
|
943
|
+
if (!Array.isArray(step))
|
|
944
|
+
return formatAtom(step);
|
|
945
|
+
if (step.length === 0)
|
|
946
|
+
return "()";
|
|
947
|
+
// Try compact first
|
|
948
|
+
var compact = "(" + step.map(function (s, i) {
|
|
949
|
+
if (Array.isArray(s))
|
|
950
|
+
return prettyFormatStep(s, indentUnit, Infinity, "");
|
|
951
|
+
return formatSexpValue(s, i === 0);
|
|
952
|
+
}).join(" ") + ")";
|
|
953
|
+
if (currentIndent.length + compact.length <= maxLineWidth)
|
|
954
|
+
return compact;
|
|
955
|
+
// Wrap: operator on first line, each arg indented
|
|
956
|
+
var op = Array.isArray(step[0]) ? prettyFormatStep(step[0], indentUnit, maxLineWidth, currentIndent) : formatSexpValue(step[0], true);
|
|
957
|
+
var args = step.slice(1);
|
|
958
|
+
if (args.length === 0)
|
|
959
|
+
return "(" + op + ")";
|
|
960
|
+
var childIndent = currentIndent + indentUnit;
|
|
961
|
+
var formattedArgs = args.map(function (a) { return prettyFormatStep(a, indentUnit, maxLineWidth, childIndent); });
|
|
962
|
+
return "(" + op + "\n" + formattedArgs.map(function (a) { return childIndent + a; }).join("\n") + ")";
|
|
963
|
+
}
|
|
964
|
+
function validOperatorNames() {
|
|
965
|
+
return Object.keys(operatorInfo).join(", ");
|
|
966
|
+
}
|
|
967
|
+
function checkArity(op, argCount, strict) {
|
|
968
|
+
if (strict === void 0) { strict = false; }
|
|
969
|
+
var info = operatorInfo[op];
|
|
970
|
+
if (!info)
|
|
971
|
+
return null;
|
|
972
|
+
if (info.min === info.max) {
|
|
973
|
+
if (argCount < info.min) {
|
|
974
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
975
|
+
}
|
|
976
|
+
if (strict && argCount > info.max) {
|
|
977
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else if (info.max === Infinity) {
|
|
981
|
+
if (argCount < info.min) {
|
|
982
|
+
return "Rule: \"" + op + "\" expects at least " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
if (argCount < info.min) {
|
|
987
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
988
|
+
}
|
|
989
|
+
if (strict && argCount > info.max) {
|
|
990
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
844
995
|
/**
|
|
845
996
|
* The `Rule` class is an experimental interface for adding behavior to {@linkcode Agent}s. A `Rule` object may be used in place of a `tick` function to be added as `Agent` behavior using `agent.set('tick', tickRule)`. As a trivial example, consider the following `Rule`, which increments the `Agent`'s `x` value with every time step:
|
|
846
997
|
*
|
|
@@ -895,6 +1046,7 @@ var Rule = /** @class */ (function () {
|
|
|
895
1046
|
* |`"agent"`|`0`|No arguments; returns the `Agent`|
|
|
896
1047
|
* |`"environment"`|`0`|No arguments, returns the `Environment`|
|
|
897
1048
|
* |`"vector"`|`any`|Creates an n-dimensional {@linkcode Vector} from the supplied arguments|
|
|
1049
|
+
* |`"log"`|`any`|Logs expression(s) and returns the last evaluated value|
|
|
898
1050
|
*/
|
|
899
1051
|
function Rule(environment, steps) {
|
|
900
1052
|
var _this = this;
|
|
@@ -903,10 +1055,12 @@ var Rule = /** @class */ (function () {
|
|
|
903
1055
|
/** @hidden */
|
|
904
1056
|
this.locals = {};
|
|
905
1057
|
/**
|
|
906
|
-
*
|
|
907
|
-
* @since 0.3.0
|
|
908
|
-
* @hidden
|
|
1058
|
+
* When true, evaluation traces are logged and captured.
|
|
909
1059
|
*/
|
|
1060
|
+
this.trace = false;
|
|
1061
|
+
this.traceLog = [];
|
|
1062
|
+
/** @hidden */
|
|
1063
|
+
this._traceDepth = 0;
|
|
910
1064
|
this.evaluate = function (agent, step) {
|
|
911
1065
|
var first = step && step.length > 0 ? step[0] : null;
|
|
912
1066
|
if (first === undefined || first === null)
|
|
@@ -917,30 +1071,79 @@ var Rule = /** @class */ (function () {
|
|
|
917
1071
|
return _this.evaluate(agent, step.slice(1));
|
|
918
1072
|
return innerStep;
|
|
919
1073
|
}
|
|
920
|
-
if
|
|
921
|
-
|
|
922
|
-
|
|
1074
|
+
// Trace: check if this is a known operator call to trace
|
|
1075
|
+
var shouldTrace = _this.trace && typeof first === "string" && first in Operators;
|
|
1076
|
+
if (shouldTrace)
|
|
1077
|
+
_this._traceDepth++;
|
|
1078
|
+
var result = _this._evaluateOp(agent, step, first);
|
|
1079
|
+
if (shouldTrace) {
|
|
1080
|
+
_this._traceLogEntry(step, result);
|
|
1081
|
+
_this._traceDepth--;
|
|
1082
|
+
}
|
|
1083
|
+
return result;
|
|
1084
|
+
};
|
|
1085
|
+
/** @hidden */
|
|
1086
|
+
this._evaluateOp = function (agent, step, first) {
|
|
1087
|
+
if (first === Operators.log) {
|
|
1088
|
+
var args = step.slice(1);
|
|
1089
|
+
if (args.length === 0) {
|
|
1090
|
+
console.warn("Rule: \"log\" expects at least 1 argument, got 0");
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
var lastValue = null;
|
|
1094
|
+
for (var i = 0; i < args.length; i++) {
|
|
1095
|
+
var expr = args[i];
|
|
1096
|
+
var evaluated = _this.evaluate(agent, [expr]);
|
|
1097
|
+
console.log("Rule log: " + formatStep(expr) + " \u2192 " + formatStep(evaluated));
|
|
1098
|
+
lastValue = evaluated;
|
|
1099
|
+
}
|
|
1100
|
+
return lastValue;
|
|
923
1101
|
}
|
|
924
1102
|
if (!(first in Operators) && step.length > 1) {
|
|
1103
|
+
if (typeof first === "string") {
|
|
1104
|
+
console.warn("Rule: unknown operator \"" + first + "\". Valid operators: " + validOperatorNames());
|
|
1105
|
+
}
|
|
925
1106
|
return step;
|
|
926
1107
|
}
|
|
1108
|
+
var argCount = step.length - 1;
|
|
1109
|
+
// Arity check at runtime for known operators
|
|
1110
|
+
if (typeof first === "string" && first in Operators) {
|
|
1111
|
+
var arityMsg = checkArity(first, argCount);
|
|
1112
|
+
if (arityMsg) {
|
|
1113
|
+
console.warn(arityMsg);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
927
1116
|
var a = step.length > 1 ? [step[1]] : undefined;
|
|
928
1117
|
var b = step.length > 2 ? [step[2]] : undefined;
|
|
929
1118
|
var c = step.length > 3 ? [step[3]] : undefined;
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1119
|
+
// Arithmetic operators with type checking
|
|
1120
|
+
if (ARITHMETIC_OPS.has(first)) {
|
|
1121
|
+
var va = _this.evaluate(agent, a);
|
|
1122
|
+
var vb = _this.evaluate(agent, b);
|
|
1123
|
+
if (typeof va !== "number" || typeof vb !== "number") {
|
|
1124
|
+
console.warn("Rule: \"" + first + "\" expects numeric arguments, got " + typeof va + " and " + typeof vb);
|
|
1125
|
+
}
|
|
1126
|
+
if (first === Operators.add)
|
|
1127
|
+
return add(va, vb);
|
|
1128
|
+
if (first === Operators.subtract)
|
|
1129
|
+
return subtract(va, vb);
|
|
1130
|
+
if (first === Operators.multiply)
|
|
1131
|
+
return multiply(va, vb);
|
|
1132
|
+
if (first === Operators.divide)
|
|
1133
|
+
return divide(va, vb);
|
|
1134
|
+
if (first === Operators.mod)
|
|
1135
|
+
return mod(va, vb);
|
|
1136
|
+
if (first === Operators.power)
|
|
1137
|
+
return power(va, vb);
|
|
1138
|
+
}
|
|
1139
|
+
if (first === Operators.get) {
|
|
1140
|
+
var keyName = _this.evaluate(agent, a);
|
|
1141
|
+
var result = get(agent, keyName);
|
|
1142
|
+
if (result === null || result === undefined) {
|
|
1143
|
+
console.warn("Rule: \"get\" returned null for key \"" + keyName + "\" \u2014 key may not exist on agent");
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
944
1147
|
if (first === Operators.set)
|
|
945
1148
|
return set(agent, _this.evaluate(agent, a), _this.evaluate(agent, b));
|
|
946
1149
|
if (first === Operators.enqueue) {
|
|
@@ -1028,13 +1231,151 @@ var Rule = /** @class */ (function () {
|
|
|
1028
1231
|
this.environment = environment;
|
|
1029
1232
|
this.steps = steps;
|
|
1030
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Format arbitrary steps as pretty-printed S-expressions.
|
|
1236
|
+
*/
|
|
1237
|
+
Rule.formatSteps = function (steps, options) {
|
|
1238
|
+
var indent = (options && options.indent) || " ";
|
|
1239
|
+
var maxLineWidth = (options && options.maxLineWidth) || 60;
|
|
1240
|
+
// Check if it's multi-step (array of arrays)
|
|
1241
|
+
var isMultiStep = steps.length > 0 && Array.isArray(steps[0]);
|
|
1242
|
+
if (isMultiStep) {
|
|
1243
|
+
return steps.map(function (s) { return prettyFormatStep(s, indent, maxLineWidth, ""); }).join("\n\n");
|
|
1244
|
+
}
|
|
1245
|
+
return prettyFormatStep(steps, indent, maxLineWidth, "");
|
|
1246
|
+
};
|
|
1247
|
+
/**
|
|
1248
|
+
* Pretty-print the rule's step tree as S-expressions.
|
|
1249
|
+
*/
|
|
1250
|
+
Rule.prototype.format = function (options) {
|
|
1251
|
+
return Rule.formatSteps(this.steps, options);
|
|
1252
|
+
};
|
|
1253
|
+
/**
|
|
1254
|
+
* Returns a formatted S-expression representation of the rule.
|
|
1255
|
+
*/
|
|
1256
|
+
Rule.prototype.toString = function () {
|
|
1257
|
+
return this.format();
|
|
1258
|
+
};
|
|
1259
|
+
/**
|
|
1260
|
+
* Validate the rule's step tree and return an array of diagnostics.
|
|
1261
|
+
* Does not throw — returns diagnostics for inspection.
|
|
1262
|
+
* @since 0.6.0
|
|
1263
|
+
*/
|
|
1264
|
+
Rule.prototype.validate = function () {
|
|
1265
|
+
var diagnostics = [];
|
|
1266
|
+
if (!this.steps || (Array.isArray(this.steps) && this.steps.length === 0)) {
|
|
1267
|
+
diagnostics.push({
|
|
1268
|
+
path: "root",
|
|
1269
|
+
level: "warning",
|
|
1270
|
+
message: "Empty steps array"
|
|
1271
|
+
});
|
|
1272
|
+
return diagnostics;
|
|
1273
|
+
}
|
|
1274
|
+
// Check if steps is a single step (first element is a string operator)
|
|
1275
|
+
// or an array of steps (first element is an array)
|
|
1276
|
+
var isMultiStep = Array.isArray(this.steps[0]);
|
|
1277
|
+
if (isMultiStep) {
|
|
1278
|
+
// Multiple top-level steps
|
|
1279
|
+
for (var i = 0; i < this.steps.length; i++) {
|
|
1280
|
+
var step = this.steps[i];
|
|
1281
|
+
if (!Array.isArray(step)) {
|
|
1282
|
+
diagnostics.push({
|
|
1283
|
+
path: "[" + i + "]",
|
|
1284
|
+
level: "warning",
|
|
1285
|
+
message: "Step is a bare value (" + typeof step + ": " + JSON.stringify(step) + ") instead of an array"
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
this._validateStep(step, "[" + i + "]", diagnostics);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
// Single step (the steps array IS the step)
|
|
1295
|
+
this._validateStep(this.steps, "root", diagnostics);
|
|
1296
|
+
}
|
|
1297
|
+
return diagnostics;
|
|
1298
|
+
};
|
|
1299
|
+
/** @hidden */
|
|
1300
|
+
Rule.prototype._validateStep = function (step, path, diagnostics) {
|
|
1301
|
+
if (step.length === 0) {
|
|
1302
|
+
diagnostics.push({
|
|
1303
|
+
path: path,
|
|
1304
|
+
level: "warning",
|
|
1305
|
+
message: "Empty step array"
|
|
1306
|
+
});
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
var first = step[0];
|
|
1310
|
+
// If first element is an array, it's a nested step sequence
|
|
1311
|
+
if (Array.isArray(first)) {
|
|
1312
|
+
this._validateStep(first, path + "[0]", diagnostics);
|
|
1313
|
+
for (var i = 1; i < step.length; i++) {
|
|
1314
|
+
if (Array.isArray(step[i])) {
|
|
1315
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if (typeof first === "string") {
|
|
1321
|
+
var argCount = step.length - 1;
|
|
1322
|
+
// Check if operator is known
|
|
1323
|
+
if (!(first in Operators)) {
|
|
1324
|
+
if (step.length > 1) {
|
|
1325
|
+
diagnostics.push({
|
|
1326
|
+
path: path,
|
|
1327
|
+
level: "error",
|
|
1328
|
+
message: "Unknown operator \"" + first + "\". Valid operators: " + validOperatorNames()
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
// Check arity (strict: also warn on too many args)
|
|
1334
|
+
var arityMsg = checkArity(first, argCount, true);
|
|
1335
|
+
if (arityMsg) {
|
|
1336
|
+
diagnostics.push({
|
|
1337
|
+
path: path,
|
|
1338
|
+
level: "error",
|
|
1339
|
+
message: arityMsg
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
// Recurse into sub-steps
|
|
1343
|
+
for (var i = 1; i < step.length; i++) {
|
|
1344
|
+
if (Array.isArray(step[i])) {
|
|
1345
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
/**
|
|
1351
|
+
* interpret single array step
|
|
1352
|
+
* @since 0.3.0
|
|
1353
|
+
* @hidden
|
|
1354
|
+
*/
|
|
1355
|
+
/** @hidden */
|
|
1356
|
+
Rule.prototype._traceLogEntry = function (step, result) {
|
|
1357
|
+
var indent = " ".repeat(this._traceDepth - 1);
|
|
1358
|
+
var formatted = formatStep(step);
|
|
1359
|
+
var resultFormatted = formatStep(result);
|
|
1360
|
+
var line = "Rule trace: " + indent + formatted + " \u2192 " + resultFormatted;
|
|
1361
|
+
console.log(line);
|
|
1362
|
+
this.traceLog.push(line);
|
|
1363
|
+
};
|
|
1031
1364
|
/**
|
|
1032
1365
|
* @since 0.3.0
|
|
1033
1366
|
* @hidden
|
|
1034
1367
|
*/
|
|
1035
1368
|
Rule.prototype.call = function (agent) {
|
|
1369
|
+
if (this.trace) {
|
|
1370
|
+
this.traceLog = [];
|
|
1371
|
+
this._traceDepth = 0;
|
|
1372
|
+
}
|
|
1036
1373
|
return this.evaluate(agent, this.steps);
|
|
1037
1374
|
};
|
|
1375
|
+
/**
|
|
1376
|
+
* Static operator info mapping operator names to their expected argument counts.
|
|
1377
|
+
*/
|
|
1378
|
+
Rule.operatorInfo = operatorInfo;
|
|
1038
1379
|
return Rule;
|
|
1039
1380
|
}());
|
|
1040
1381
|
|
|
@@ -3212,6 +3553,15 @@ var Environment = /** @class */ (function (_super) {
|
|
|
3212
3553
|
* such as a {@linkcode LineChartRenderer}, {@linkcode Histogram}, etc.
|
|
3213
3554
|
*/
|
|
3214
3555
|
_this.renderers = [];
|
|
3556
|
+
/**
|
|
3557
|
+
* Whether the `Environment` tick cycle is currently playing.
|
|
3558
|
+
* Use {@linkcode pause}, {@linkcode resume}, or {@linkcode toggle}
|
|
3559
|
+
* to control playback.
|
|
3560
|
+
* @since 0.5.22
|
|
3561
|
+
*/
|
|
3562
|
+
_this.playing = true;
|
|
3563
|
+
/** @hidden */
|
|
3564
|
+
_this._tickIntervalId = null;
|
|
3215
3565
|
/**
|
|
3216
3566
|
* This property will always equal the number of tick cycles that
|
|
3217
3567
|
* have passed since the `Environment` was created. If you call
|
|
@@ -3381,6 +3731,9 @@ var Environment = /** @class */ (function (_super) {
|
|
|
3381
3731
|
* @since 0.0.5
|
|
3382
3732
|
*/
|
|
3383
3733
|
Environment.prototype.tick = function (opts) {
|
|
3734
|
+
// If paused, skip the tick cycle (use `step()` to advance manually)
|
|
3735
|
+
if (!this.playing)
|
|
3736
|
+
return;
|
|
3384
3737
|
var _a = this._getTickOptions(opts), activation = _a.activation, activationCount = _a.activationCount, count = _a.count, randomizeOrder = _a.randomizeOrder;
|
|
3385
3738
|
// for uniform activation, every agent is always activated
|
|
3386
3739
|
if (activation === "uniform") {
|
|
@@ -3448,6 +3801,47 @@ var Environment = /** @class */ (function (_super) {
|
|
|
3448
3801
|
}
|
|
3449
3802
|
this.renderers.forEach(function (r) { return r.render(); });
|
|
3450
3803
|
};
|
|
3804
|
+
/**
|
|
3805
|
+
* Pause the tick cycle. While paused, calling {@linkcode tick} will
|
|
3806
|
+
* be a no-op unless you use {@linkcode step} to advance manually.
|
|
3807
|
+
* @since 0.5.22
|
|
3808
|
+
*/
|
|
3809
|
+
Environment.prototype.pause = function () {
|
|
3810
|
+
this.playing = false;
|
|
3811
|
+
};
|
|
3812
|
+
/**
|
|
3813
|
+
* Resume the tick cycle after it has been paused.
|
|
3814
|
+
* @since 0.5.22
|
|
3815
|
+
*/
|
|
3816
|
+
Environment.prototype.resume = function () {
|
|
3817
|
+
this.playing = true;
|
|
3818
|
+
};
|
|
3819
|
+
/**
|
|
3820
|
+
* Toggle the tick cycle between playing and paused.
|
|
3821
|
+
* @since 0.5.22
|
|
3822
|
+
*/
|
|
3823
|
+
Environment.prototype.toggle = function () {
|
|
3824
|
+
this.playing = !this.playing;
|
|
3825
|
+
};
|
|
3826
|
+
/**
|
|
3827
|
+
* Advance the `Environment` by exactly one tick, regardless of whether
|
|
3828
|
+
* it is paused. This is useful for stepping through the simulation
|
|
3829
|
+
* frame-by-frame while paused.
|
|
3830
|
+
*
|
|
3831
|
+
* ```js
|
|
3832
|
+
* environment.pause();
|
|
3833
|
+
* environment.step(); // advances one tick
|
|
3834
|
+
* ```
|
|
3835
|
+
*
|
|
3836
|
+
* @since 0.5.22
|
|
3837
|
+
*/
|
|
3838
|
+
Environment.prototype.step = function (opts) {
|
|
3839
|
+
// Temporarily mark as playing so tick executes, then restore
|
|
3840
|
+
var wasPlaying = this.playing;
|
|
3841
|
+
this.playing = true;
|
|
3842
|
+
this.tick(opts);
|
|
3843
|
+
this.playing = wasPlaying;
|
|
3844
|
+
};
|
|
3451
3845
|
/**
|
|
3452
3846
|
* Use a helper with this environment. A helper can be one of:
|
|
3453
3847
|
* - {@linkcode KDTree}
|
|
@@ -3984,7 +4378,10 @@ var defaultOptions = {
|
|
|
3984
4378
|
width: 500,
|
|
3985
4379
|
height: 500,
|
|
3986
4380
|
scale: 1,
|
|
3987
|
-
trace: false
|
|
4381
|
+
trace: false,
|
|
4382
|
+
interactive: false,
|
|
4383
|
+
zoomMin: 0.1,
|
|
4384
|
+
zoomMax: 10
|
|
3988
4385
|
};
|
|
3989
4386
|
/**
|
|
3990
4387
|
* A `CanvasRenderer` renders an {@linkcode Environment} spatially in two dimensions.
|
|
@@ -4004,6 +4401,12 @@ var defaultOptions = {
|
|
|
4004
4401
|
* - `"triangle"` — Draws a triangle centered at the `Agent`'s `"x"` / `"y"` values.
|
|
4005
4402
|
* - Also uses the `"size"` value.
|
|
4006
4403
|
*
|
|
4404
|
+
* When `interactive` is set to `true` in the options, the renderer supports:
|
|
4405
|
+
* - **Click/hover detection** — Use {@linkcode on} to listen for `"click"`, `"hover"`, and `"unhover"` events on agents.
|
|
4406
|
+
* - **Agent selection** — Clicking an agent selects it (highlighted with a stroke). Access selected agents via {@linkcode selected}.
|
|
4407
|
+
* - **Pan** — Click and drag on empty space to pan.
|
|
4408
|
+
* - **Zoom** — Scroll to zoom in/out (bounded by `zoomMin` / `zoomMax`).
|
|
4409
|
+
*
|
|
4007
4410
|
* @since 0.0.11
|
|
4008
4411
|
*/
|
|
4009
4412
|
var CanvasRenderer = /** @class */ (function (_super) {
|
|
@@ -4019,15 +4422,33 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4019
4422
|
* - `connectionOpacity` (*number* = `1`) — For `Environment`s using a `Network`, the opacity of lines
|
|
4020
4423
|
* - `connectionWidth` (*number* = `1`) — For `Environment`s using a `Network`, the width of lines
|
|
4021
4424
|
* - `height` (*number* = `500`) — The height, in pixels, of the canvas on which to render
|
|
4425
|
+
* - `interactive` (*boolean* = `false`) — Enables interactive features (click/hover detection, selection, pan, zoom)
|
|
4426
|
+
* - `onSelect` (*function*) — Optional callback when an agent is selected or deselected
|
|
4022
4427
|
* - `origin` (*{ x: number; y: number }* = `{ x: 0, y: 0 }`) — The coordinate of the upper-left point of the space to be rendered
|
|
4023
4428
|
* - `scale` (*number* = `1`) — The scale at which to render (the larger the scale, the smaller the size of the space that is actually rendered)
|
|
4024
4429
|
* - `trace` (*boolean* = `false`) — If `true`, the renderer will not clear old drawings, causing the `Agent`s to appear to *trace* their paths across space
|
|
4025
4430
|
* - `width` (*number* = `500`) — The width, in pixels, of the canvas on which to render
|
|
4431
|
+
* - `zoomMin` (*number* = `0.1`) — Minimum scale when zooming
|
|
4432
|
+
* - `zoomMax` (*number* = `10`) — Maximum scale when zooming
|
|
4026
4433
|
*/
|
|
4027
4434
|
function CanvasRenderer(environment, opts) {
|
|
4028
4435
|
var _this = _super.call(this) || this;
|
|
4029
4436
|
/** @hidden */
|
|
4030
4437
|
_this.terrainBuffer = document.createElement("canvas");
|
|
4438
|
+
/** The currently selected agents (only used when `interactive` is `true`). */
|
|
4439
|
+
_this.selected = [];
|
|
4440
|
+
/** @hidden */
|
|
4441
|
+
_this._listeners = new Map();
|
|
4442
|
+
/** @hidden */
|
|
4443
|
+
_this._hoveredAgent = null;
|
|
4444
|
+
/** @hidden */
|
|
4445
|
+
_this._isPanning = false;
|
|
4446
|
+
/** @hidden */
|
|
4447
|
+
_this._panStart = null;
|
|
4448
|
+
/** @hidden */
|
|
4449
|
+
_this._panOriginStart = null;
|
|
4450
|
+
/** @hidden */
|
|
4451
|
+
_this._boundHandlers = {};
|
|
4031
4452
|
_this.environment = environment;
|
|
4032
4453
|
environment.renderers.push(_this);
|
|
4033
4454
|
_this.opts = Object.assign({}, defaultOptions);
|
|
@@ -4044,9 +4465,158 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4044
4465
|
_this.terrainBuffer.width = width;
|
|
4045
4466
|
_this.terrainBuffer.height = height;
|
|
4046
4467
|
_this.context.fillStyle = opts.background;
|
|
4047
|
-
_this.context.fillRect(0, 0, width, height);
|
|
4468
|
+
_this.context.fillRect(0, 0, _this.width, _this.height);
|
|
4469
|
+
if (_this.opts.interactive) {
|
|
4470
|
+
_this._setupInteractiveListeners();
|
|
4471
|
+
}
|
|
4048
4472
|
return _this;
|
|
4049
4473
|
}
|
|
4474
|
+
/**
|
|
4475
|
+
* Register a callback for an interactive event.
|
|
4476
|
+
* Supported event names: `"click"`, `"hover"`, `"unhover"`.
|
|
4477
|
+
*
|
|
4478
|
+
* ```js
|
|
4479
|
+
* renderer.on("click", (agent, event) => {
|
|
4480
|
+
* console.log("Clicked agent:", agent.id);
|
|
4481
|
+
* });
|
|
4482
|
+
* ```
|
|
4483
|
+
*
|
|
4484
|
+
* @param eventName - The event to listen for.
|
|
4485
|
+
* @param callback - The callback, invoked with the `Agent` and the `MouseEvent`.
|
|
4486
|
+
*/
|
|
4487
|
+
CanvasRenderer.prototype.on = function (eventName, callback) {
|
|
4488
|
+
if (!this._listeners.has(eventName)) {
|
|
4489
|
+
this._listeners.set(eventName, []);
|
|
4490
|
+
}
|
|
4491
|
+
this._listeners.get(eventName).push(callback);
|
|
4492
|
+
};
|
|
4493
|
+
/** @hidden */
|
|
4494
|
+
CanvasRenderer.prototype._emit = function (eventName, agent, event) {
|
|
4495
|
+
var callbacks = this._listeners.get(eventName);
|
|
4496
|
+
if (callbacks) {
|
|
4497
|
+
callbacks.forEach(function (cb) { return cb(agent, event); });
|
|
4498
|
+
}
|
|
4499
|
+
};
|
|
4500
|
+
/**
|
|
4501
|
+
* Given a mouse event, return the agent at that position (if any).
|
|
4502
|
+
* Hit-testing accounts for the agent's shape and size.
|
|
4503
|
+
* @hidden
|
|
4504
|
+
*/
|
|
4505
|
+
CanvasRenderer.prototype._agentAtPoint = function (clientX, clientY) {
|
|
4506
|
+
var rect = this.canvas.getBoundingClientRect();
|
|
4507
|
+
var dpr = window.devicePixelRatio;
|
|
4508
|
+
var canvasX = (clientX - rect.left) * dpr;
|
|
4509
|
+
var canvasY = (clientY - rect.top) * dpr;
|
|
4510
|
+
var agents = this.environment.getAgents();
|
|
4511
|
+
// Iterate in reverse so topmost-drawn agent is found first
|
|
4512
|
+
for (var i = agents.length - 1; i >= 0; i--) {
|
|
4513
|
+
var agent = agents[i];
|
|
4514
|
+
var data = agent.getData();
|
|
4515
|
+
var ax = this.x(data.x);
|
|
4516
|
+
var ay = this.y(data.y);
|
|
4517
|
+
var shape = data.shape;
|
|
4518
|
+
var size = (data.size || 1) * dpr;
|
|
4519
|
+
if (shape === "rect") {
|
|
4520
|
+
var w = (data.width || 1) * dpr;
|
|
4521
|
+
var h = (data.height || 1) * dpr;
|
|
4522
|
+
var rx = ax - w / 2;
|
|
4523
|
+
var ry = ay - h / 2;
|
|
4524
|
+
if (canvasX >= rx && canvasX <= rx + w && canvasY >= ry && canvasY <= ry + h) {
|
|
4525
|
+
return agent;
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
else if (shape === "triangle") {
|
|
4529
|
+
// Simple bounding-box hit test for triangles
|
|
4530
|
+
var halfSize = size / 2;
|
|
4531
|
+
if (canvasX >= ax - halfSize &&
|
|
4532
|
+
canvasX <= ax + halfSize &&
|
|
4533
|
+
canvasY >= ay - halfSize &&
|
|
4534
|
+
canvasY <= ay + halfSize) {
|
|
4535
|
+
return agent;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
else {
|
|
4539
|
+
// Default: circle (and arrow) — distance-based hit test
|
|
4540
|
+
var dx = canvasX - ax;
|
|
4541
|
+
var dy = canvasY - ay;
|
|
4542
|
+
var hitRadius = Math.max(size, 4 * dpr); // minimum hit area for tiny agents
|
|
4543
|
+
if (dx * dx + dy * dy <= hitRadius * hitRadius) {
|
|
4544
|
+
return agent;
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
return null;
|
|
4549
|
+
};
|
|
4550
|
+
/** @hidden */
|
|
4551
|
+
CanvasRenderer.prototype._setupInteractiveListeners = function () {
|
|
4552
|
+
var _this = this;
|
|
4553
|
+
var onMouseDown = function (e) {
|
|
4554
|
+
var agent = _this._agentAtPoint(e.clientX, e.clientY);
|
|
4555
|
+
if (agent) {
|
|
4556
|
+
// Agent click — select it
|
|
4557
|
+
_this.selected = [agent];
|
|
4558
|
+
if (_this.opts.onSelect)
|
|
4559
|
+
_this.opts.onSelect(agent);
|
|
4560
|
+
_this._emit("click", agent, e);
|
|
4561
|
+
_this.render();
|
|
4562
|
+
}
|
|
4563
|
+
else {
|
|
4564
|
+
// Empty space — deselect and start panning
|
|
4565
|
+
if (_this.selected.length > 0) {
|
|
4566
|
+
_this.selected = [];
|
|
4567
|
+
if (_this.opts.onSelect)
|
|
4568
|
+
_this.opts.onSelect(null);
|
|
4569
|
+
_this.render();
|
|
4570
|
+
}
|
|
4571
|
+
_this._isPanning = true;
|
|
4572
|
+
_this._panStart = { x: e.clientX, y: e.clientY };
|
|
4573
|
+
_this._panOriginStart = { x: _this.opts.origin.x, y: _this.opts.origin.y };
|
|
4574
|
+
}
|
|
4575
|
+
};
|
|
4576
|
+
var onMouseMove = function (e) {
|
|
4577
|
+
if (_this._isPanning && _this._panStart && _this._panOriginStart) {
|
|
4578
|
+
var dpr = window.devicePixelRatio;
|
|
4579
|
+
var dx = e.clientX - _this._panStart.x;
|
|
4580
|
+
var dy = e.clientY - _this._panStart.y;
|
|
4581
|
+
_this.opts.origin = {
|
|
4582
|
+
x: _this._panOriginStart.x - dx / (_this.opts.scale * dpr),
|
|
4583
|
+
y: _this._panOriginStart.y - dy / (_this.opts.scale * dpr)
|
|
4584
|
+
};
|
|
4585
|
+
_this.render();
|
|
4586
|
+
return;
|
|
4587
|
+
}
|
|
4588
|
+
// Hover detection
|
|
4589
|
+
var agent = _this._agentAtPoint(e.clientX, e.clientY);
|
|
4590
|
+
if (agent !== _this._hoveredAgent) {
|
|
4591
|
+
if (_this._hoveredAgent) {
|
|
4592
|
+
_this._emit("unhover", _this._hoveredAgent, e);
|
|
4593
|
+
}
|
|
4594
|
+
if (agent) {
|
|
4595
|
+
_this._emit("hover", agent, e);
|
|
4596
|
+
}
|
|
4597
|
+
_this._hoveredAgent = agent;
|
|
4598
|
+
}
|
|
4599
|
+
};
|
|
4600
|
+
var onMouseUp = function (e) {
|
|
4601
|
+
_this._isPanning = false;
|
|
4602
|
+
_this._panStart = null;
|
|
4603
|
+
_this._panOriginStart = null;
|
|
4604
|
+
};
|
|
4605
|
+
var onWheel = function (e) {
|
|
4606
|
+
e.preventDefault();
|
|
4607
|
+
var _a = _this.opts, zoomMin = _a.zoomMin, zoomMax = _a.zoomMax;
|
|
4608
|
+
var delta = e.deltaY > 0 ? 0.9 : 1.1;
|
|
4609
|
+
var newScale = _this.opts.scale * delta;
|
|
4610
|
+
newScale = Math.max(zoomMin, Math.min(zoomMax, newScale));
|
|
4611
|
+
_this.opts.scale = newScale;
|
|
4612
|
+
_this.render();
|
|
4613
|
+
};
|
|
4614
|
+
this._boundHandlers = { mousedown: onMouseDown, mousemove: onMouseMove, mouseup: onMouseUp, wheel: onWheel };
|
|
4615
|
+
this.canvas.addEventListener("mousedown", onMouseDown);
|
|
4616
|
+
this.canvas.addEventListener("mousemove", onMouseMove);
|
|
4617
|
+
this.canvas.addEventListener("mouseup", onMouseUp);
|
|
4618
|
+
this.canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
4619
|
+
};
|
|
4050
4620
|
/** @hidden */
|
|
4051
4621
|
CanvasRenderer.prototype.x = function (v) {
|
|
4052
4622
|
var _a = this.opts, origin = _a.origin, scale = _a.scale;
|
|
@@ -4083,21 +4653,21 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4083
4653
|
};
|
|
4084
4654
|
/** @hidden */
|
|
4085
4655
|
CanvasRenderer.prototype.drawPathWrap = function (points) {
|
|
4086
|
-
var _this = this;
|
|
4087
4656
|
var _a = this, width = _a.width, height = _a.height;
|
|
4088
4657
|
var right = false;
|
|
4089
4658
|
var left = false;
|
|
4090
4659
|
var lower = false;
|
|
4091
4660
|
var upper = false;
|
|
4661
|
+
// points are already in DPR-scaled pixel space, so compare directly
|
|
4092
4662
|
points.forEach(function (_a) {
|
|
4093
4663
|
var px = _a[0], py = _a[1];
|
|
4094
|
-
if (
|
|
4664
|
+
if (px >= width)
|
|
4095
4665
|
right = true;
|
|
4096
|
-
if (
|
|
4666
|
+
if (px < 0)
|
|
4097
4667
|
left = true;
|
|
4098
|
-
if (
|
|
4668
|
+
if (py >= height)
|
|
4099
4669
|
lower = true;
|
|
4100
|
-
if (
|
|
4670
|
+
if (py < 0)
|
|
4101
4671
|
upper = true;
|
|
4102
4672
|
});
|
|
4103
4673
|
if (right)
|
|
@@ -4126,24 +4696,26 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4126
4696
|
/** @hidden */
|
|
4127
4697
|
CanvasRenderer.prototype.drawCircleWrap = function (x, y, size) {
|
|
4128
4698
|
var _a = this, width = _a.width, height = _a.height;
|
|
4699
|
+
var worldWidth = this.opts.width;
|
|
4700
|
+
var worldHeight = this.opts.height;
|
|
4129
4701
|
if (this.x(x + size) >= width) {
|
|
4130
|
-
this.drawCircle(x -
|
|
4702
|
+
this.drawCircle(x - worldWidth, y, size);
|
|
4131
4703
|
if (this.y(y + size) >= height)
|
|
4132
|
-
this.drawCircle(x -
|
|
4704
|
+
this.drawCircle(x - worldWidth, y - worldHeight, size);
|
|
4133
4705
|
if (this.y(y - size) < 0)
|
|
4134
|
-
this.drawCircle(x -
|
|
4706
|
+
this.drawCircle(x - worldWidth, y + worldHeight, size);
|
|
4135
4707
|
}
|
|
4136
4708
|
if (this.x(x - size) < 0) {
|
|
4137
|
-
this.drawCircle(x +
|
|
4709
|
+
this.drawCircle(x + worldWidth, y, size);
|
|
4138
4710
|
if (this.y(y + size) >= height)
|
|
4139
|
-
this.drawCircle(x +
|
|
4711
|
+
this.drawCircle(x + worldWidth, y - worldHeight, size);
|
|
4140
4712
|
if (this.y(y - size) < 0)
|
|
4141
|
-
this.drawCircle(x +
|
|
4713
|
+
this.drawCircle(x + worldWidth, y + worldHeight, size);
|
|
4142
4714
|
}
|
|
4143
4715
|
if (this.y(y + size) > height)
|
|
4144
|
-
this.drawCircle(x, y -
|
|
4716
|
+
this.drawCircle(x, y - worldHeight, size);
|
|
4145
4717
|
if (this.y(y - size) < 0)
|
|
4146
|
-
this.drawCircle(x, y +
|
|
4718
|
+
this.drawCircle(x, y + worldHeight, size);
|
|
4147
4719
|
};
|
|
4148
4720
|
/**
|
|
4149
4721
|
* Draw a rectangle centered at (x, y). Automatically calculates the offset
|
|
@@ -4157,25 +4729,55 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4157
4729
|
};
|
|
4158
4730
|
/** @hidden */
|
|
4159
4731
|
CanvasRenderer.prototype.drawRectWrap = function (x, y, w, h) {
|
|
4160
|
-
var _a = this
|
|
4732
|
+
var _a = this, width = _a.width, height = _a.height;
|
|
4733
|
+
var worldWidth = this.opts.width;
|
|
4734
|
+
var worldHeight = this.opts.height;
|
|
4161
4735
|
if (this.x(x + w / 2) >= width) {
|
|
4162
|
-
this.drawRect(x -
|
|
4736
|
+
this.drawRect(x - worldWidth, y, w, h);
|
|
4163
4737
|
if (this.y(y + h / 2) >= height)
|
|
4164
|
-
this.drawRect(x -
|
|
4165
|
-
if (this.y(y -
|
|
4166
|
-
this.drawRect(x -
|
|
4738
|
+
this.drawRect(x - worldWidth, y - worldHeight, w, h);
|
|
4739
|
+
if (this.y(y - h / 2) < 0)
|
|
4740
|
+
this.drawRect(x - worldWidth, y + worldHeight, w, h);
|
|
4167
4741
|
}
|
|
4168
4742
|
if (this.x(x - w / 2) < 0) {
|
|
4169
|
-
this.drawRect(x +
|
|
4743
|
+
this.drawRect(x + worldWidth, y, w, h);
|
|
4170
4744
|
if (this.y(y + h / 2) >= height)
|
|
4171
|
-
this.drawRect(x +
|
|
4172
|
-
if (this.y(y -
|
|
4173
|
-
this.drawRect(x +
|
|
4745
|
+
this.drawRect(x + worldWidth, y - worldHeight, w, h);
|
|
4746
|
+
if (this.y(y - h / 2) < 0)
|
|
4747
|
+
this.drawRect(x + worldWidth, y + worldHeight, w, h);
|
|
4174
4748
|
}
|
|
4175
4749
|
if (this.y(y + h / 2) > height)
|
|
4176
|
-
this.drawRect(x, y -
|
|
4177
|
-
if (this.y(y -
|
|
4178
|
-
this.drawRect(x, y +
|
|
4750
|
+
this.drawRect(x, y - worldHeight, w, h);
|
|
4751
|
+
if (this.y(y - h / 2) < 0)
|
|
4752
|
+
this.drawRect(x, y + worldHeight, w, h);
|
|
4753
|
+
};
|
|
4754
|
+
/**
|
|
4755
|
+
* Draw a selection highlight around the given agent.
|
|
4756
|
+
* @hidden
|
|
4757
|
+
*/
|
|
4758
|
+
CanvasRenderer.prototype._drawSelectionHighlight = function (agent) {
|
|
4759
|
+
var bufferContext = this.buffer.getContext("2d");
|
|
4760
|
+
var dpr = window.devicePixelRatio;
|
|
4761
|
+
var data = agent.getData();
|
|
4762
|
+
var ax = this.x(data.x);
|
|
4763
|
+
var ay = this.y(data.y);
|
|
4764
|
+
var shape = data.shape;
|
|
4765
|
+
var size = (data.size || 1) * dpr;
|
|
4766
|
+
bufferContext.save();
|
|
4767
|
+
bufferContext.strokeStyle = "#0af";
|
|
4768
|
+
bufferContext.lineWidth = 2 * dpr;
|
|
4769
|
+
if (shape === "rect") {
|
|
4770
|
+
var w = (data.width || 1) * dpr;
|
|
4771
|
+
var h = (data.height || 1) * dpr;
|
|
4772
|
+
bufferContext.strokeRect(ax - w / 2 - 2 * dpr, ay - h / 2 - 2 * dpr, w + 4 * dpr, h + 4 * dpr);
|
|
4773
|
+
}
|
|
4774
|
+
else {
|
|
4775
|
+
bufferContext.beginPath();
|
|
4776
|
+
var highlightRadius = Math.max(size, 4 * dpr) + 3 * dpr;
|
|
4777
|
+
bufferContext.arc(ax, ay, highlightRadius, 0, 2 * Math.PI);
|
|
4778
|
+
bufferContext.stroke();
|
|
4779
|
+
}
|
|
4780
|
+
bufferContext.restore();
|
|
4179
4781
|
};
|
|
4180
4782
|
CanvasRenderer.prototype.render = function () {
|
|
4181
4783
|
var _this = this;
|
|
@@ -4188,22 +4790,24 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4188
4790
|
// if "trace" is truthy, don't clear the canvas with every frame
|
|
4189
4791
|
// to trace the paths of agents
|
|
4190
4792
|
if (!trace) {
|
|
4191
|
-
context.clearRect(0, 0, width
|
|
4793
|
+
context.clearRect(0, 0, width, height);
|
|
4192
4794
|
context.fillStyle = opts.background;
|
|
4193
|
-
context.fillRect(0, 0, width
|
|
4795
|
+
context.fillRect(0, 0, width, height);
|
|
4194
4796
|
}
|
|
4195
4797
|
// automatically position agents in an environment that uses a network helper
|
|
4196
4798
|
if (opts.autoPosition && environment.helpers.network) {
|
|
4197
4799
|
environment.getAgents().forEach(function (agent) {
|
|
4198
4800
|
var network = _this.environment.helpers.network;
|
|
4199
|
-
|
|
4801
|
+
// Use CSS pixel dimensions (opts), not the DPI-scaled canvas dimensions,
|
|
4802
|
+
// since x() and y() already apply the devicePixelRatio transform.
|
|
4803
|
+
var _a = _this.opts, w = _a.width, h = _a.height;
|
|
4200
4804
|
// only set once
|
|
4201
4805
|
if ((agent.get("x") === null || agent.get("y") === null) &&
|
|
4202
4806
|
network.isInNetwork(agent)) {
|
|
4203
4807
|
var idx = network.indexOf(agent);
|
|
4204
4808
|
var angle = idx / network.agents.length;
|
|
4205
|
-
var x =
|
|
4206
|
-
var y =
|
|
4809
|
+
var x = w / 2 + 0.4 * w * Math.cos(2 * Math.PI * angle);
|
|
4810
|
+
var y = h / 2 + 0.4 * h * Math.sin(2 * Math.PI * angle);
|
|
4207
4811
|
agent.set({ x: x, y: y });
|
|
4208
4812
|
}
|
|
4209
4813
|
});
|
|
@@ -4248,8 +4852,8 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4248
4852
|
context.globalAlpha = _this.opts.connectionOpacity;
|
|
4249
4853
|
context.strokeStyle = _this.opts.connectionColor;
|
|
4250
4854
|
context.lineWidth = _this.opts.connectionWidth;
|
|
4251
|
-
context.moveTo(_this.x(x), _this.
|
|
4252
|
-
context.lineTo(_this.x(nx), _this.
|
|
4855
|
+
context.moveTo(_this.x(x), _this.y(y));
|
|
4856
|
+
context.lineTo(_this.x(nx), _this.y(ny));
|
|
4253
4857
|
context.stroke();
|
|
4254
4858
|
context.closePath();
|
|
4255
4859
|
context.restore();
|
|
@@ -4281,10 +4885,11 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4281
4885
|
}
|
|
4282
4886
|
else if (shape === "triangle") {
|
|
4283
4887
|
bufferContext.beginPath();
|
|
4888
|
+
var scaledSize = size * dpr;
|
|
4284
4889
|
var points = [
|
|
4285
|
-
[_this.x(x), _this.y(y) -
|
|
4286
|
-
[_this.x(x) +
|
|
4287
|
-
[_this.x(x) -
|
|
4890
|
+
[_this.x(x), _this.y(y) - scaledSize / 2],
|
|
4891
|
+
[_this.x(x) + scaledSize / 2, _this.y(y) + scaledSize / 2],
|
|
4892
|
+
[_this.x(x) - scaledSize / 2, _this.y(y) + scaledSize / 2]
|
|
4288
4893
|
];
|
|
4289
4894
|
_this.drawPath(points);
|
|
4290
4895
|
if (environment.opts.torus)
|
|
@@ -4310,6 +4915,12 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
4310
4915
|
bufferContext.restore();
|
|
4311
4916
|
}
|
|
4312
4917
|
});
|
|
4918
|
+
// Draw selection highlights for selected agents
|
|
4919
|
+
if (opts.interactive && this.selected.length > 0) {
|
|
4920
|
+
this.selected.forEach(function (agent) {
|
|
4921
|
+
_this._drawSelectionHighlight(agent);
|
|
4922
|
+
});
|
|
4923
|
+
}
|
|
4313
4924
|
context.drawImage(buffer, 0, 0);
|
|
4314
4925
|
};
|
|
4315
4926
|
return CanvasRenderer;
|
|
@@ -4400,11 +5011,13 @@ var Histogram = /** @class */ (function (_super) {
|
|
|
4400
5011
|
};
|
|
4401
5012
|
Histogram.prototype.x = function (value) {
|
|
4402
5013
|
var _a = this, width = _a.width, markerWidth = _a.markerWidth;
|
|
4403
|
-
|
|
5014
|
+
var dpr = window.devicePixelRatio;
|
|
5015
|
+
return remap(value, 0, width, markerWidth + PADDING_AT_LEFT * dpr, width - PADDING_AT_RIGHT * dpr);
|
|
4404
5016
|
};
|
|
4405
5017
|
Histogram.prototype.y = function (value) {
|
|
4406
5018
|
var _a = this, height = _a.height, maxValue = _a.maxValue;
|
|
4407
|
-
|
|
5019
|
+
var dpr = window.devicePixelRatio;
|
|
5020
|
+
return remap(value, 0, maxValue, height - PADDING_AT_BOTTOM * dpr, 0);
|
|
4408
5021
|
};
|
|
4409
5022
|
Histogram.prototype.setMaxValue = function () {
|
|
4410
5023
|
var _this = this;
|
|
@@ -4433,11 +5046,12 @@ var Histogram = /** @class */ (function (_super) {
|
|
|
4433
5046
|
var context = this.canvas.getContext("2d");
|
|
4434
5047
|
var _a = this, height = _a.height, width = _a.width;
|
|
4435
5048
|
var _b = this.opts, aboveMax = _b.aboveMax, belowMin = _b.belowMin, buckets = _b.buckets, min = _b.min, max$1 = _b.max;
|
|
5049
|
+
var dpr = window.devicePixelRatio;
|
|
4436
5050
|
var yMin = 0;
|
|
4437
5051
|
var yMax = this.maxValue;
|
|
4438
5052
|
var markers = extractRoundNumbers({ min: yMin, max: yMax });
|
|
4439
5053
|
context.fillStyle = "black";
|
|
4440
|
-
context.font = 14 *
|
|
5054
|
+
context.font = 14 * dpr + "px Helvetica";
|
|
4441
5055
|
// determine the width of the longest marker
|
|
4442
5056
|
this.markerWidth = max(markers.map(function (marker) { return context.measureText(marker.toLocaleString()).width; }));
|
|
4443
5057
|
// draw horizontal lines
|
|
@@ -4446,9 +5060,9 @@ var Histogram = /** @class */ (function (_super) {
|
|
|
4446
5060
|
context.textBaseline = "middle";
|
|
4447
5061
|
context.fillText(marker.toLocaleString(), _this.markerWidth, _this.y(marker));
|
|
4448
5062
|
context.beginPath();
|
|
4449
|
-
context.moveTo(_this.markerWidth + 10, _this.y(marker));
|
|
5063
|
+
context.moveTo(_this.markerWidth + 10 * dpr, _this.y(marker));
|
|
4450
5064
|
context.lineTo(_this.width, _this.y(marker));
|
|
4451
|
-
context.setLineDash(LINE_DASH);
|
|
5065
|
+
context.setLineDash(LINE_DASH.map(function (v) { return v * dpr; }));
|
|
4452
5066
|
context.stroke();
|
|
4453
5067
|
});
|
|
4454
5068
|
var numBuckets = bucketValues.length - (aboveMax ? 1 : 0) - (belowMin ? 1 : 0);
|
|
@@ -4471,9 +5085,9 @@ var Histogram = /** @class */ (function (_super) {
|
|
|
4471
5085
|
.forEach(function (label, i) {
|
|
4472
5086
|
context.save();
|
|
4473
5087
|
context.translate(_this.x((i * width) / bucketValues.length +
|
|
4474
|
-
(0.5 * width) / bucketValues.length), height - 50);
|
|
5088
|
+
(0.5 * width) / bucketValues.length), height - 50 * dpr);
|
|
4475
5089
|
context.rotate(Math.PI / 4);
|
|
4476
|
-
context.font = 12 *
|
|
5090
|
+
context.font = 12 * dpr + "px Helvetica";
|
|
4477
5091
|
context.textAlign = "left";
|
|
4478
5092
|
context.textBaseline = "middle";
|
|
4479
5093
|
context.fillText(label, 0, 0);
|
|
@@ -4483,22 +5097,23 @@ var Histogram = /** @class */ (function (_super) {
|
|
|
4483
5097
|
Histogram.prototype.drawBuckets = function (bucketValues, offset) {
|
|
4484
5098
|
var _this = this;
|
|
4485
5099
|
if (offset === void 0) { offset = 0; }
|
|
4486
|
-
var canvas =
|
|
5100
|
+
var _a = this, canvas = _a.canvas, width = _a.width, height = _a.height;
|
|
4487
5101
|
var metric = this._metric;
|
|
4488
5102
|
var numMetrics = Array.isArray(metric) ? metric.length : 1;
|
|
4489
|
-
var
|
|
5103
|
+
var _b = this.opts, aboveMax = _b.aboveMax, belowMin = _b.belowMin, color = _b.color;
|
|
5104
|
+
var dpr = window.devicePixelRatio;
|
|
4490
5105
|
var context = canvas.getContext("2d");
|
|
4491
5106
|
context.fillStyle = Array.isArray(color)
|
|
4492
5107
|
? color[offset % color.length]
|
|
4493
5108
|
: color;
|
|
4494
5109
|
var numBuckets = bucketValues.length;
|
|
4495
|
-
var barWidth = (width - PADDING_AT_LEFT - PADDING_AT_RIGHT - this.markerWidth) /
|
|
5110
|
+
var barWidth = (width - PADDING_AT_LEFT * dpr - PADDING_AT_RIGHT * dpr - this.markerWidth) /
|
|
4496
5111
|
numBuckets;
|
|
4497
5112
|
barWidth *= 0.8;
|
|
4498
5113
|
bucketValues.forEach(function (value, i) {
|
|
4499
5114
|
var mappedValue = remap(value, 0, _this.maxValue, 0, 1);
|
|
4500
5115
|
var x = _this.x(((0.1 + i) * width) / numBuckets);
|
|
4501
|
-
context.fillRect(x + (offset * barWidth - (numMetrics - 1)) / numMetrics + offset, remap(mappedValue, 0, 1, height - PADDING_AT_BOTTOM, 0), barWidth / numMetrics, remap(mappedValue, 0, 1, 0, height - PADDING_AT_BOTTOM));
|
|
5116
|
+
context.fillRect(x + (offset * barWidth - (numMetrics - 1)) / numMetrics + offset, remap(mappedValue, 0, 1, height - PADDING_AT_BOTTOM * dpr, 0), barWidth / numMetrics, remap(mappedValue, 0, 1, 0, height - PADDING_AT_BOTTOM * dpr));
|
|
4502
5117
|
});
|
|
4503
5118
|
};
|
|
4504
5119
|
Histogram.prototype.getBucketValues = function (metric) {
|
|
@@ -4635,12 +5250,16 @@ var LineChartRenderer = /** @class */ (function (_super) {
|
|
|
4635
5250
|
var height = this.height;
|
|
4636
5251
|
var range = this.opts.range;
|
|
4637
5252
|
var min = range.min, max = range.max;
|
|
4638
|
-
var
|
|
4639
|
-
|
|
5253
|
+
var dpr = window.devicePixelRatio;
|
|
5254
|
+
var paddingBottom = PADDING_BOTTOM * dpr;
|
|
5255
|
+
var pxPerUnit = (height - 2 * paddingBottom) / (max - min);
|
|
5256
|
+
return Math.round(height - (value - min) * pxPerUnit) - 2 * paddingBottom;
|
|
4640
5257
|
};
|
|
4641
5258
|
LineChartRenderer.prototype.drawBackground = function () {
|
|
4642
5259
|
var _this = this;
|
|
4643
5260
|
var _a = this, context = _a.context, width = _a.width, height = _a.height, opts = _a.opts, t = _a.t;
|
|
5261
|
+
var dpr = window.devicePixelRatio;
|
|
5262
|
+
var paddingBottom = PADDING_BOTTOM * dpr;
|
|
4644
5263
|
// draw background and lines
|
|
4645
5264
|
context.fillStyle = this.opts.background;
|
|
4646
5265
|
context.fillRect(0, 0, width, height);
|
|
@@ -4648,27 +5267,27 @@ var LineChartRenderer = /** @class */ (function (_super) {
|
|
|
4648
5267
|
var markers = extractRoundNumbers(range);
|
|
4649
5268
|
var textMaxWidth = 0;
|
|
4650
5269
|
// write values on vertical axis
|
|
4651
|
-
context.font = 14 *
|
|
5270
|
+
context.font = 14 * dpr + "px Helvetica";
|
|
4652
5271
|
context.fillStyle = "#000";
|
|
4653
5272
|
context.textBaseline = "middle";
|
|
4654
5273
|
markers.forEach(function (marker) {
|
|
4655
|
-
if (_this.y(marker) < 10 || _this.y(marker) + 10 > height)
|
|
5274
|
+
if (_this.y(marker) < 10 * dpr || _this.y(marker) + 10 * dpr > height)
|
|
4656
5275
|
return;
|
|
4657
5276
|
var width = context.measureText(marker.toLocaleString()).width;
|
|
4658
5277
|
if (width > textMaxWidth)
|
|
4659
5278
|
textMaxWidth = width;
|
|
4660
|
-
context.fillText(marker.toLocaleString(), 5, _this.y(marker));
|
|
5279
|
+
context.fillText(marker.toLocaleString(), 5 * dpr, _this.y(marker));
|
|
4661
5280
|
});
|
|
4662
5281
|
// draw horizontal lines for vertical axis
|
|
4663
5282
|
context.save();
|
|
4664
5283
|
context.strokeStyle = "#999";
|
|
4665
5284
|
markers.forEach(function (marker) {
|
|
4666
|
-
if (_this.y(marker) >= height -
|
|
5285
|
+
if (_this.y(marker) >= height - paddingBottom)
|
|
4667
5286
|
return;
|
|
4668
5287
|
context.beginPath();
|
|
4669
|
-
context.moveTo(textMaxWidth + 10, _this.y(marker));
|
|
5288
|
+
context.moveTo(textMaxWidth + 10 * dpr, _this.y(marker));
|
|
4670
5289
|
context.lineTo(_this.x(Math.max(width, _this.environment.time)), _this.y(marker));
|
|
4671
|
-
context.setLineDash(lineDash);
|
|
5290
|
+
context.setLineDash(lineDash.map(function (v) { return v * dpr; }));
|
|
4672
5291
|
context.stroke();
|
|
4673
5292
|
});
|
|
4674
5293
|
context.restore();
|
|
@@ -4685,12 +5304,12 @@ var LineChartRenderer = /** @class */ (function (_super) {
|
|
|
4685
5304
|
_this.x(marker) - width / 2 < textMaxWidth) {
|
|
4686
5305
|
return;
|
|
4687
5306
|
}
|
|
4688
|
-
context.font = 11 *
|
|
4689
|
-
context.fillText(marker.toLocaleString(), _this.x(marker), height -
|
|
5307
|
+
context.font = 11 * dpr + "px Helvetica";
|
|
5308
|
+
context.fillText(marker.toLocaleString(), _this.x(marker), height - paddingBottom);
|
|
4690
5309
|
context.strokeStyle = "black";
|
|
4691
5310
|
context.lineWidth = 1;
|
|
4692
5311
|
context.beginPath();
|
|
4693
|
-
context.moveTo(_this.x(marker), height - 4);
|
|
5312
|
+
context.moveTo(_this.x(marker), height - 4 * dpr);
|
|
4694
5313
|
context.lineTo(_this.x(marker), height);
|
|
4695
5314
|
context.stroke();
|
|
4696
5315
|
});
|
|
@@ -5038,7 +5657,8 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5038
5657
|
*/
|
|
5039
5658
|
Heatmap.prototype.x = function (value) {
|
|
5040
5659
|
var width = this.width;
|
|
5041
|
-
|
|
5660
|
+
var dpr = window.devicePixelRatio;
|
|
5661
|
+
return remap(value, this.getMin("x"), this.getMax("x"), PADDING_AT_LEFT$1 * dpr, width);
|
|
5042
5662
|
};
|
|
5043
5663
|
/**
|
|
5044
5664
|
* Map a value (on the range y-min to y-max) onto canvas space to draw it along the y-axis.
|
|
@@ -5046,7 +5666,8 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5046
5666
|
*/
|
|
5047
5667
|
Heatmap.prototype.y = function (value) {
|
|
5048
5668
|
var height = this.height;
|
|
5049
|
-
|
|
5669
|
+
var dpr = window.devicePixelRatio;
|
|
5670
|
+
return remap(value, this.getMin("y"), this.getMax("y"), height - PADDING_AT_BOTTOM$1 * dpr, 0);
|
|
5050
5671
|
};
|
|
5051
5672
|
/** @hidden */
|
|
5052
5673
|
Heatmap.prototype.getKey = function (axis) {
|
|
@@ -5089,52 +5710,55 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5089
5710
|
Heatmap.prototype.drawMarkers = function () {
|
|
5090
5711
|
var _a = this, context = _a.context, width = _a.width, height = _a.height;
|
|
5091
5712
|
var _b = this.opts, from = _b.from, to = _b.to;
|
|
5713
|
+
var dpr = window.devicePixelRatio;
|
|
5714
|
+
var padLeft = PADDING_AT_LEFT$1 * dpr;
|
|
5715
|
+
var padBottom = PADDING_AT_BOTTOM$1 * dpr;
|
|
5092
5716
|
context.strokeStyle = "black";
|
|
5093
5717
|
context.lineWidth = 1;
|
|
5094
|
-
context.moveTo(
|
|
5095
|
-
context.lineTo(
|
|
5096
|
-
context.lineTo(width, height -
|
|
5718
|
+
context.moveTo(padLeft - 1, 0);
|
|
5719
|
+
context.lineTo(padLeft - 1, height - padBottom + 1);
|
|
5720
|
+
context.lineTo(width, height - padBottom + 1);
|
|
5097
5721
|
context.stroke();
|
|
5098
5722
|
context.lineWidth = 0;
|
|
5099
|
-
var gradient = context.createLinearGradient(10, 0,
|
|
5723
|
+
var gradient = context.createLinearGradient(10 * dpr, 0, padLeft - 10 * dpr, 0);
|
|
5100
5724
|
gradient.addColorStop(0, from);
|
|
5101
5725
|
gradient.addColorStop(1, to);
|
|
5102
5726
|
context.fillStyle = gradient;
|
|
5103
|
-
context.fillRect(10, height -
|
|
5727
|
+
context.fillRect(10 * dpr, height - padBottom + 20 * dpr, padLeft - 24 * dpr, 20 * dpr);
|
|
5104
5728
|
context.fillStyle = "black";
|
|
5105
5729
|
var step = (this.getMax("x") - this.getMin("x")) / this.getBuckets("x");
|
|
5106
5730
|
var originalStep = step;
|
|
5107
|
-
while (Math.abs(this.x(step) - this.x(0)) < 35)
|
|
5731
|
+
while (Math.abs(this.x(step) - this.x(0)) < 35 * dpr)
|
|
5108
5732
|
step *= 2;
|
|
5109
5733
|
for (var marker = this.getMin("x"); marker <= this.getMax("x"); marker += originalStep) {
|
|
5110
|
-
if (this.x(marker) + 10 > width)
|
|
5734
|
+
if (this.x(marker) + 10 * dpr > width)
|
|
5111
5735
|
continue;
|
|
5112
|
-
context.moveTo(this.x(marker), height -
|
|
5113
|
-
context.lineTo(this.x(marker), height -
|
|
5736
|
+
context.moveTo(this.x(marker), height - padBottom);
|
|
5737
|
+
context.lineTo(this.x(marker), height - padBottom + 10 * dpr);
|
|
5114
5738
|
context.stroke();
|
|
5115
5739
|
if (Math.abs(((marker - this.getMin("x")) / step) % 1) < 0.001 ||
|
|
5116
5740
|
Math.abs((((marker - this.getMin("x")) / step) % 1) - 1) < 0.001) {
|
|
5117
|
-
context.font = 12 *
|
|
5741
|
+
context.font = 12 * dpr + "px Helvetica";
|
|
5118
5742
|
context.textAlign = "center";
|
|
5119
|
-
context.fillText(marker.toLocaleString(), this.x(marker), height -
|
|
5743
|
+
context.fillText(marker.toLocaleString(), this.x(marker), height - padBottom + 24 * dpr);
|
|
5120
5744
|
}
|
|
5121
5745
|
}
|
|
5122
5746
|
step = (this.getMax("y") - this.getMin("y")) / this.getBuckets("y");
|
|
5123
5747
|
originalStep = step;
|
|
5124
|
-
while (Math.abs(this.y(step) - this.y(0)) < 20)
|
|
5748
|
+
while (Math.abs(this.y(step) - this.y(0)) < 20 * dpr)
|
|
5125
5749
|
step *= 2;
|
|
5126
5750
|
for (var marker = this.getMin("y"); marker <= this.getMax("y"); marker += originalStep) {
|
|
5127
|
-
if (this.y(marker) - 10 < 0)
|
|
5751
|
+
if (this.y(marker) - 10 * dpr < 0)
|
|
5128
5752
|
continue;
|
|
5129
|
-
context.moveTo(
|
|
5130
|
-
context.lineTo(
|
|
5753
|
+
context.moveTo(padLeft, this.y(marker));
|
|
5754
|
+
context.lineTo(padLeft - 10 * dpr, this.y(marker));
|
|
5131
5755
|
context.stroke();
|
|
5132
5756
|
if (Math.abs(((marker - this.getMin("y")) / step) % 1) < 0.001 ||
|
|
5133
5757
|
Math.abs((((marker - this.getMin("y")) / step) % 1) - 1) < 0.001) {
|
|
5134
|
-
context.font = 12 *
|
|
5758
|
+
context.font = 12 * dpr + "px Helvetica";
|
|
5135
5759
|
context.textAlign = "right";
|
|
5136
5760
|
context.textBaseline = "middle";
|
|
5137
|
-
context.fillText(marker.toLocaleString(),
|
|
5761
|
+
context.fillText(marker.toLocaleString(), padLeft - 14 * dpr, this.y(marker));
|
|
5138
5762
|
}
|
|
5139
5763
|
}
|
|
5140
5764
|
};
|
|
@@ -5142,6 +5766,8 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5142
5766
|
Heatmap.prototype.updateScale = function () {
|
|
5143
5767
|
var _a = this, context = _a.context, environment = _a.environment, height = _a.height;
|
|
5144
5768
|
var scale = this.opts.scale;
|
|
5769
|
+
var dpr = window.devicePixelRatio;
|
|
5770
|
+
var padLeft = PADDING_AT_LEFT$1 * dpr;
|
|
5145
5771
|
var max = scale === "relative" ? this.localMax : this.opts.max;
|
|
5146
5772
|
if (max === undefined) {
|
|
5147
5773
|
if (!this.lastUpdatedScale) {
|
|
@@ -5150,13 +5776,13 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5150
5776
|
max = environment.getAgents().length;
|
|
5151
5777
|
}
|
|
5152
5778
|
if (!this.lastUpdatedScale || +new Date() - +this.lastUpdatedScale > 250) {
|
|
5153
|
-
context.clearRect(0, height - 20,
|
|
5779
|
+
context.clearRect(0, height - 20 * dpr, padLeft, 20 * dpr);
|
|
5154
5780
|
context.fillStyle = "black";
|
|
5155
|
-
context.font = 12 *
|
|
5781
|
+
context.font = 12 * dpr + "px Helvetica";
|
|
5156
5782
|
context.textAlign = "center";
|
|
5157
5783
|
context.textBaseline = "bottom";
|
|
5158
|
-
context.fillText("0", 10, height - 5);
|
|
5159
|
-
context.fillText(max.toString(),
|
|
5784
|
+
context.fillText("0", 10 * dpr, height - 5 * dpr);
|
|
5785
|
+
context.fillText(max.toString(), padLeft - 16 * dpr, height - 5 * dpr);
|
|
5160
5786
|
this.lastUpdatedScale = new Date();
|
|
5161
5787
|
}
|
|
5162
5788
|
};
|
|
@@ -5164,6 +5790,9 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5164
5790
|
Heatmap.prototype.drawRectangles = function () {
|
|
5165
5791
|
var _a = this, canvas = _a.canvas, environment = _a.environment, width = _a.width, height = _a.height;
|
|
5166
5792
|
var _b = this.opts, scale = _b.scale, from = _b.from, to = _b.to;
|
|
5793
|
+
var dpr = window.devicePixelRatio;
|
|
5794
|
+
var padLeft = PADDING_AT_LEFT$1 * dpr;
|
|
5795
|
+
var padBottom = PADDING_AT_BOTTOM$1 * dpr;
|
|
5167
5796
|
var context = canvas.getContext("2d");
|
|
5168
5797
|
var xBuckets = this.getBuckets("x");
|
|
5169
5798
|
var yBuckets = this.getBuckets("y");
|
|
@@ -5172,9 +5801,9 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5172
5801
|
max = environment.getAgents().length;
|
|
5173
5802
|
// clear background by drawing background rectangle
|
|
5174
5803
|
context.fillStyle = from;
|
|
5175
|
-
context.fillRect(
|
|
5176
|
-
var w = width / xBuckets;
|
|
5177
|
-
var h = height / yBuckets;
|
|
5804
|
+
context.fillRect(padLeft, 0, width - padLeft, height - padBottom);
|
|
5805
|
+
var w = (width - padLeft) / xBuckets;
|
|
5806
|
+
var h = (height - padBottom) / yBuckets;
|
|
5178
5807
|
for (var row = 0; row < yBuckets; row++) {
|
|
5179
5808
|
for (var column = 0; column < xBuckets; column++) {
|
|
5180
5809
|
var index = row * xBuckets + column;
|
|
@@ -5182,7 +5811,7 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5182
5811
|
var a = clamp(remap(this.buckets[index], 0, max, 0, 1), 0, 1);
|
|
5183
5812
|
context.fillStyle = to;
|
|
5184
5813
|
context.globalAlpha = a;
|
|
5185
|
-
context.fillRect(this.x(remap(column, 0, xBuckets, this.getMin("x"), this.getMax("x"))), this.y(remap(row, -1, yBuckets - 1, this.getMin("y"), this.getMax("y"))),
|
|
5814
|
+
context.fillRect(this.x(remap(column, 0, xBuckets, this.getMin("x"), this.getMax("x"))), this.y(remap(row, -1, yBuckets - 1, this.getMin("y"), this.getMax("y"))), w, h);
|
|
5186
5815
|
}
|
|
5187
5816
|
}
|
|
5188
5817
|
context.globalAlpha = 1;
|
|
@@ -5238,6 +5867,6 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5238
5867
|
/**
|
|
5239
5868
|
* The current version of the Flocc library.
|
|
5240
5869
|
*/
|
|
5241
|
-
var version = "0.5.
|
|
5870
|
+
var version = "0.5.23";
|
|
5242
5871
|
|
|
5243
5872
|
export { ASCIIRenderer, Agent, CanvasRenderer, Colors, Environment, GridEnvironment, Heatmap, Histogram, KDTree, LineChartRenderer, Network, NumArray, Rule, TableRenderer, Terrain, version as VERSION, Vector, utils };
|