Block visualization color-by-age mode
This commit is contained in:
@@ -8,14 +8,11 @@ import { Color, Position } from './sprite-types';
|
||||
import { Price } from '../../services/price.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { defaultColorFunction, setOpacity, defaultFeeColors, defaultAuditFeeColors, defaultMarginalFeeColors, defaultAuditColors } from './utils';
|
||||
import { defaultColorFunction, setOpacity, defaultAuditColors, defaultColors } from './utils';
|
||||
import { ActiveFilter, FilterMode, toFlags } from '../../shared/filters.utils';
|
||||
import { detectWebGL } from '../../shared/graphs.utils';
|
||||
|
||||
const unmatchedOpacity = 0.2;
|
||||
const unmatchedFeeColors = defaultFeeColors.map(c => setOpacity(c, unmatchedOpacity));
|
||||
const unmatchedAuditFeeColors = defaultAuditFeeColors.map(c => setOpacity(c, unmatchedOpacity));
|
||||
const unmatchedMarginalFeeColors = defaultMarginalFeeColors.map(c => setOpacity(c, unmatchedOpacity));
|
||||
const unmatchedAuditColors = {
|
||||
censored: setOpacity(defaultAuditColors.censored, unmatchedOpacity),
|
||||
missing: setOpacity(defaultAuditColors.missing, unmatchedOpacity),
|
||||
@@ -46,6 +43,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
@Input() excludeFilters: string[] = [];
|
||||
@Input() filterFlags: bigint | null = null;
|
||||
@Input() filterMode: FilterMode = 'and';
|
||||
@Input() gradientMode: 'fee' | 'age' = 'fee';
|
||||
@Input() relativeTime: number | null;
|
||||
@Input() blockConversion: Price;
|
||||
@Input() overrideColors: ((tx: TxView) => Color) | null = null;
|
||||
@@ -121,21 +119,22 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
this.setHighlightingEnabled(this.auditHighlighting);
|
||||
}
|
||||
if (changes.overrideColor && this.scene) {
|
||||
this.scene.setColorFunction(this.overrideColors);
|
||||
this.scene.setColorFunction(this.getFilterColorFunction(0n, this.gradientMode));
|
||||
}
|
||||
if ((changes.filterFlags || changes.showFilters || changes.filterMode)) {
|
||||
if ((changes.filterFlags || changes.showFilters || changes.filterMode || changes.gradientMode)) {
|
||||
this.setFilterFlags();
|
||||
}
|
||||
}
|
||||
|
||||
setFilterFlags(goggle?: ActiveFilter): void {
|
||||
this.filterMode = goggle?.mode || this.filterMode;
|
||||
this.gradientMode = goggle?.gradient || this.gradientMode;
|
||||
this.activeFilterFlags = goggle?.filters ? toFlags(goggle.filters) : this.filterFlags;
|
||||
if (this.scene) {
|
||||
if (this.activeFilterFlags != null && this.filtersAvailable) {
|
||||
this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags));
|
||||
this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags, this.gradientMode));
|
||||
} else {
|
||||
this.scene.setColorFunction(this.overrideColors);
|
||||
this.scene.setColorFunction(this.getFilterColorFunction(0n, this.gradientMode));
|
||||
}
|
||||
}
|
||||
this.start();
|
||||
@@ -212,6 +211,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
remove = remove.filter(txid => this.scene.txs[txid]);
|
||||
change = change.filter(tx => this.scene.txs[tx.txid]);
|
||||
|
||||
if (this.gradientMode === 'age') {
|
||||
this.scene.updateAllColors();
|
||||
}
|
||||
this.scene.update(add, remove, change, direction, resetLayout);
|
||||
this.start();
|
||||
this.updateSearchHighlight();
|
||||
@@ -548,25 +550,24 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
|
||||
getColorFunction(): ((tx: TxView) => Color) {
|
||||
if (this.filterFlags) {
|
||||
return this.getFilterColorFunction(this.filterFlags);
|
||||
return this.getFilterColorFunction(this.filterFlags, this.gradientMode);
|
||||
} else if (this.activeFilterFlags) {
|
||||
return this.getFilterColorFunction(this.activeFilterFlags);
|
||||
return this.getFilterColorFunction(this.activeFilterFlags, this.gradientMode);
|
||||
} else {
|
||||
return this.overrideColors;
|
||||
return this.getFilterColorFunction(0n, this.gradientMode);
|
||||
}
|
||||
}
|
||||
|
||||
getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) {
|
||||
getFilterColorFunction(flags: bigint, gradient: 'fee' | 'age'): ((tx: TxView) => Color) {
|
||||
return (tx: TxView) => {
|
||||
if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (flags === 0n || (tx.bigintFlags & flags) > 0n))) {
|
||||
return defaultColorFunction(tx);
|
||||
return defaultColorFunction(tx, defaultColors[gradient], defaultAuditColors, this.relativeTime || (Date.now() / 1000));
|
||||
} else {
|
||||
return defaultColorFunction(
|
||||
tx,
|
||||
unmatchedFeeColors,
|
||||
unmatchedAuditFeeColors,
|
||||
unmatchedMarginalFeeColors,
|
||||
unmatchedAuditColors
|
||||
defaultColors['unmatched' + gradient],
|
||||
unmatchedAuditColors,
|
||||
this.relativeTime || (Date.now() / 1000)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,6 +68,10 @@ export default class BlockScene {
|
||||
|
||||
setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void {
|
||||
this.getColor = colorFunction || defaultColorFunction;
|
||||
this.updateAllColors();
|
||||
}
|
||||
|
||||
updateAllColors(): void {
|
||||
this.dirty = true;
|
||||
if (this.initialised && this.scene) {
|
||||
this.updateColors(performance.now(), 50);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { feeLevels, mempoolFeeColors } from '../../app.constants';
|
||||
import { feeLevels, mempoolAgeColors, mempoolFeeColors } from '../../app.constants';
|
||||
import { Color } from './sprite-types';
|
||||
import TxView from './tx-view';
|
||||
|
||||
@@ -37,10 +37,42 @@ export function setOpacity(color: Color, opacity: number): Color {
|
||||
};
|
||||
}
|
||||
|
||||
interface ColorPalette {
|
||||
base: Color[],
|
||||
audit: Color[],
|
||||
marginal: Color[],
|
||||
baseLevel: (tx: TxView, rate: number, time: number) => number,
|
||||
}
|
||||
|
||||
// precomputed colors
|
||||
export const defaultFeeColors = mempoolFeeColors.map(hexToColor);
|
||||
export const defaultAuditFeeColors = defaultFeeColors.map((color) => darken(desaturate(color, 0.3), 0.9));
|
||||
export const defaultMarginalFeeColors = defaultFeeColors.map((color) => darken(desaturate(color, 0.8), 1.1));
|
||||
const defaultColors: { [key: string]: ColorPalette } = {
|
||||
fee: {
|
||||
base: mempoolFeeColors.map(hexToColor),
|
||||
audit: [],
|
||||
marginal: [],
|
||||
baseLevel: (tx: TxView, rate: number) => feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1
|
||||
},
|
||||
age: {
|
||||
base: mempoolAgeColors.map(hexToColor),
|
||||
audit: [],
|
||||
marginal: [],
|
||||
baseLevel: (tx: TxView, rate: number, relativeTime: number) => (!tx.time ? 0 : Math.max(0, Math.round(1.25 * Math.log2((Math.max(1, relativeTime - tx.time))))))
|
||||
},
|
||||
}
|
||||
for (const key in defaultColors) {
|
||||
const base = defaultColors[key].base;
|
||||
defaultColors[key].audit = base.map((color) => darken(desaturate(color, 0.3), 0.9));
|
||||
defaultColors[key].marginal = base.map((color) => darken(desaturate(color, 0.8), 1.1));
|
||||
defaultColors['unmatched' + key] = {
|
||||
base: defaultColors[key].base.map(c => setOpacity(c, 0.2)),
|
||||
audit: defaultColors[key].audit.map(c => setOpacity(c, 0.2)),
|
||||
marginal: defaultColors[key].marginal.map(c => setOpacity(c, 0.2)),
|
||||
baseLevel: defaultColors[key].baseLevel,
|
||||
};
|
||||
}
|
||||
|
||||
export { defaultColors as defaultColors };
|
||||
|
||||
export const defaultAuditColors = {
|
||||
censored: hexToColor('f344df'),
|
||||
missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7),
|
||||
@@ -51,22 +83,21 @@ export const defaultAuditColors = {
|
||||
|
||||
export function defaultColorFunction(
|
||||
tx: TxView,
|
||||
feeColors: Color[] = defaultFeeColors,
|
||||
auditFeeColors: Color[] = defaultAuditFeeColors,
|
||||
marginalFeeColors: Color[] = defaultMarginalFeeColors,
|
||||
auditColors: { [status: string]: Color } = defaultAuditColors
|
||||
colors: { base: Color[], audit: Color[], marginal: Color[], baseLevel: (tx: TxView, rate: number, time: number) => number } = defaultColors.fee,
|
||||
auditColors: { [status: string]: Color } = defaultAuditColors,
|
||||
relativeTime?: number,
|
||||
): Color {
|
||||
const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate
|
||||
const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1;
|
||||
const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1];
|
||||
const levelIndex = colors.baseLevel(tx, rate, relativeTime || (Date.now() / 1000));
|
||||
const levelColor = colors.base[levelIndex] || colors.base[mempoolFeeColors.length - 1];
|
||||
// Normal mode
|
||||
if (!tx.scene?.highlightingEnabled) {
|
||||
if (tx.acc) {
|
||||
return auditColors.accelerated;
|
||||
} else {
|
||||
return feeLevelColor;
|
||||
return levelColor;
|
||||
}
|
||||
return feeLevelColor;
|
||||
return levelColor;
|
||||
}
|
||||
// Block audit
|
||||
switch(tx.status) {
|
||||
@@ -75,7 +106,7 @@ export function defaultColorFunction(
|
||||
case 'missing':
|
||||
case 'sigop':
|
||||
case 'rbf':
|
||||
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
|
||||
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
|
||||
case 'fresh':
|
||||
case 'freshcpfp':
|
||||
return auditColors.missing;
|
||||
@@ -84,20 +115,20 @@ export function defaultColorFunction(
|
||||
case 'prioritized':
|
||||
return auditColors.prioritized;
|
||||
case 'selected':
|
||||
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
|
||||
return colors.marginal[levelIndex] || colors.marginal[mempoolFeeColors.length - 1];
|
||||
case 'accelerated':
|
||||
return auditColors.accelerated;
|
||||
case 'found':
|
||||
if (tx.context === 'projected') {
|
||||
return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1];
|
||||
return colors.audit[levelIndex] || colors.audit[mempoolFeeColors.length - 1];
|
||||
} else {
|
||||
return feeLevelColor;
|
||||
return levelColor;
|
||||
}
|
||||
default:
|
||||
if (tx.acc) {
|
||||
return auditColors.accelerated;
|
||||
} else {
|
||||
return feeLevelColor;
|
||||
return levelColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user