Speed up layer generation script

This commit is contained in:
Pieter Vander Vennet 2023-10-31 11:49:14 +01:00
parent 5bcc617d22
commit fa83a51df5
5 changed files with 100 additions and 79 deletions

View file

@ -308,9 +308,30 @@ export class FirstOf<T, X> extends Conversion<T, X> {
} }
} }
export class Cached<TIn, TOut> extends Conversion<TIn, TOut> {
private _step: Conversion<TIn, TOut>
private readonly key: string
constructor(step: Conversion<TIn, TOut>) {
super("Secretly caches the output for the given input", [], "cached")
this._step = step
this.key = "__super_secret_caching_key_" + step.name
}
convert(json: TIn, context: ConversionContext): TOut {
if (json[this.key]) {
return json[this.key]
}
const converted = this._step.convert(json, context)
Object.defineProperty(json, this.key, {
value: converted,
enumerable: false,
})
return converted
}
}
export class Fuse<T> extends DesugaringStep<T> { export class Fuse<T> extends DesugaringStep<T> {
private readonly steps: DesugaringStep<T>[] private readonly steps: DesugaringStep<T>[]
protected debug = false
constructor(doc: string, ...steps: DesugaringStep<T>[]) { constructor(doc: string, ...steps: DesugaringStep<T>[]) {
super( super(
(doc ?? "") + (doc ?? "") +
@ -322,8 +343,15 @@ export class Fuse<T> extends DesugaringStep<T> {
this.steps = Utils.NoNull(steps) this.steps = Utils.NoNull(steps)
} }
public enableDebugging(): Fuse<T> {
this.debug = true
return this
}
convert(json: T, context: ConversionContext): T { convert(json: T, context: ConversionContext): T {
const timings = []
for (let i = 0; i < this.steps.length; i++) { for (let i = 0; i < this.steps.length; i++) {
const start = new Date()
const step = this.steps[i] const step = this.steps[i]
try { try {
const r = step.convert(json, context.inOperation(step.name)) const r = step.convert(json, context.inOperation(step.name))
@ -335,6 +363,14 @@ export class Fuse<T> extends DesugaringStep<T> {
console.error("Step " + step.name + " failed due to ", e, e.stack) console.error("Step " + step.name + " failed due to ", e, e.stack)
throw e throw e
} }
if (this.debug) {
const stop = new Date()
const timeNeededMs = stop.getTime() - start.getTime()
timings.push(timeNeededMs)
}
}
if (this.debug) {
console.log("Time needed,", timings.join(", "))
} }
return json return json
} }

View file

@ -1,4 +1,5 @@
import { import {
Cached,
Concat, Concat,
Conversion, Conversion,
ConversionContext, ConversionContext,
@ -31,6 +32,7 @@ import { RenderingSpecification } from "../../../UI/SpecialVisualization"
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
import { ConfigMeta } from "../../../UI/Studio/configMeta" import { ConfigMeta } from "../../../UI/Studio/configMeta"
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson" import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
import { j } from "vite-node/types-63205a44"
class ExpandFilter extends DesugaringStep<LayerConfigJson> { class ExpandFilter extends DesugaringStep<LayerConfigJson> {
private static readonly predefinedFilters = ExpandFilter.load_filters() private static readonly predefinedFilters = ExpandFilter.load_filters()
@ -483,14 +485,13 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
) { ) {
return json return json
} }
json = JSON.parse(JSON.stringify(json)) json = { ...json }
const allSpecials: Exclude<RenderingSpecification, string>[] = [] json.tagRenderings = [...json.tagRenderings]
.concat( const allSpecials: Exclude<RenderingSpecification, string>[] = <any>(
...json.tagRenderings.map((tr) => ValidationUtils.getAllSpecialVisualisations(<any>json.tagRenderings).filter(
ValidationUtils.getSpecialVisualsationsWithArgs(<TagRenderingConfigJson>tr) (spec) => typeof spec !== "string"
)
) )
.filter((spec) => typeof spec !== "string") )
const questionSpecials = allSpecials.filter((sp) => sp.func.funcName === "questions") const questionSpecials = allSpecials.filter((sp) => sp.func.funcName === "questions")
const noLabels = questionSpecials.filter( const noLabels = questionSpecials.filter(
@ -579,18 +580,34 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
if (this._desugaring.tagRenderings === null) { if (this._desugaring.tagRenderings === null) {
return json return json
} }
json = JSON.parse(JSON.stringify(json)) if (json.source === "special") {
return json
}
if (!json.title && !json.tagRenderings) {
return json
}
json = { ...json }
json.tagRenderings = [...(json.tagRenderings ?? [])]
const specialVisualisations = ValidationUtils.getAllSpecialVisualisations(
<any>json.tagRenderings
)
const usedSpecialFunctions = new Set(
specialVisualisations.map((sv) =>
typeof sv === "string" ? undefined : sv.func.funcName
)
)
if (!usedSpecialFunctions.has("minimap")) {
json.tagRenderings.push(this._desugaring.tagRenderings.get("minimap"))
}
if ( if (
json.tagRenderings &&
this._desugaring.tagRenderings.has("just_created") && this._desugaring.tagRenderings.has("just_created") &&
!json.tagRenderings.some((tr) => tr === "just_created" || tr["id"] === "just_created") !json.tagRenderings.some((tr) => tr === "just_created" || tr["id"] === "just_created")
) { ) {
json.tagRenderings.unshift(this._desugaring.tagRenderings.get("just_created")) json.tagRenderings.unshift(this._desugaring.tagRenderings.get("just_created"))
} }
if (json.allowSplit && !ValidationUtils.hasSpecialVisualisation(json, "split_button")) { if (json.allowSplit && !usedSpecialFunctions.has("split_button")) {
json.tagRenderings ??= []
json.tagRenderings.push({ json.tagRenderings.push({
id: "split-button", id: "split-button",
render: { "*": "{split_button()}" }, render: { "*": "{split_button()}" },
@ -598,14 +615,13 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
delete json.allowSplit delete json.allowSplit
} }
if (json.allowMove && !ValidationUtils.hasSpecialVisualisation(json, "move_button")) { if (json.allowMove && !usedSpecialFunctions.has("move_button")) {
json.tagRenderings ??= []
json.tagRenderings.push({ json.tagRenderings.push({
id: "move-button", id: "move-button",
render: { "*": "{move_button()}" }, render: { "*": "{move_button()}" },
}) })
} }
if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "delete_button")) { if (json.deletion && !usedSpecialFunctions.has("delete_button")) {
json.tagRenderings.push({ json.tagRenderings.push({
id: "delete-button", id: "delete-button",
render: { "*": "{delete_button()}" }, render: { "*": "{delete_button()}" },
@ -622,7 +638,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
json.tagRenderings.push(this._desugaring.tagRenderings.get("last_edit")) json.tagRenderings.push(this._desugaring.tagRenderings.get("last_edit"))
} }
if (!ValidationUtils.hasSpecialVisualisation(json, "all_tags")) { if (!usedSpecialFunctions.has("all_tags")) {
const trc: QuestionableTagRenderingConfigJson = { const trc: QuestionableTagRenderingConfigJson = {
id: "all-tags", id: "all-tags",
render: { "*": "{all_tags()}" }, render: { "*": "{all_tags()}" },
@ -1141,41 +1157,6 @@ class SetFullNodeDatabase extends DesugaringStep<LayerConfigJson> {
} }
} }
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
private readonly _state: DesugaringContext
constructor(state: DesugaringContext) {
super(
"Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap",
["tagRenderings"],
"AddMiniMap"
)
this._state = state
}
convert(layerConfig: LayerConfigJson, context: ConversionContext): LayerConfigJson {
if (!layerConfig.tagRenderings || layerConfig.source === "special") {
return layerConfig
}
const state = this._state
const hasMinimap = ValidationUtils.hasSpecialVisualisation(layerConfig, "minimap")
if (!hasMinimap) {
layerConfig = { ...layerConfig }
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
const minimap = state.tagRenderings.get("minimap")
if (minimap === undefined) {
if (state.tagRenderings.size > 0) {
throw "The 'minimap'-builtin tagrendering is not defined. As such, it cannot be added automatically"
}
} else {
layerConfig.tagRenderings.push(minimap)
}
}
return layerConfig
}
}
class ExpandMarkerRenderings extends DesugaringStep<IconConfigJson> { class ExpandMarkerRenderings extends DesugaringStep<IconConfigJson> {
private readonly _layer: LayerConfigJson private readonly _layer: LayerConfigJson
private readonly _state: DesugaringContext private readonly _state: DesugaringContext
@ -1211,16 +1192,15 @@ class ExpandMarkerRenderings extends DesugaringStep<IconConfigJson> {
} }
} }
export class PrepareLayer extends Fuse<LayerConfigJson> { export class PrepareLayer extends Cached<LayerConfigJson, LayerConfigJson> {
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
super( const steps = new Fuse<LayerConfigJson>(
"Fully prepares and expands a layer for the LayerConfig.", "Fully prepares and expands a layer for the LayerConfig.",
new On("tagRenderings", new Each(new RewriteSpecial())), new On("tagRenderings", new Each(new RewriteSpecial())),
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))), new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
new On("tagRenderings", new Each(new DetectInline())), new On("tagRenderings", new Each(new DetectInline())),
new AddQuestionBox(), new AddQuestionBox(),
new AddMiniMap(state),
new AddEditingElements(state), new AddEditingElements(state),
new SetFullNodeDatabase(), new SetFullNodeDatabase(),
new On< new On<
@ -1244,5 +1224,6 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
), ),
new ExpandFilter(state) new ExpandFilter(state)
) )
super(steps)
} }
} }

View file

@ -275,8 +275,7 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
doesImageExist: DoesImageExist, doesImageExist: DoesImageExist,
path: string, path: string,
isBuiltin: boolean, isBuiltin: boolean,
sharedTagRenderings?: Set<string>, sharedTagRenderings?: Set<string>
msg?: string
) { ) {
super( super(
"Validates a theme and the contained layers", "Validates a theme and the contained layers",
@ -287,8 +286,7 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
new Pipe( new Pipe(
new ValidateLayer(undefined, isBuiltin, doesImageExist, false, true), new ValidateLayer(undefined, isBuiltin, doesImageExist, false, true),
new Pure((x) => x.raw) new Pure((x) => x.raw)
), )
msg
) )
) )
) )

View file

@ -2,25 +2,17 @@ import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import SpecialVisualizations from "../../../UI/SpecialVisualizations" import SpecialVisualizations from "../../../UI/SpecialVisualizations"
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization" import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
import { LayerConfigJson } from "../Json/LayerConfigJson" import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
export default class ValidationUtils { export default class ValidationUtils {
public static hasSpecialVisualisation( public static getAllSpecialVisualisations(
layer: LayerConfigJson, renderingConfigs: (TagRenderingConfigJson | QuestionableTagRenderingConfigJson)[]
specialVisualisation: string ): RenderingSpecification[] {
): boolean { const visualisations: RenderingSpecification[] = []
return ( for (const renderConfig of renderingConfigs) {
layer.tagRenderings?.some((tagRendering) => { visualisations.push(...ValidationUtils.getSpecialVisualisationsWithArgs(renderConfig))
if (tagRendering === undefined) { }
return false return visualisations
}
const spec = ValidationUtils.getSpecialVisualisations(
<TagRenderingConfigJson>tagRendering
)
return spec.some((vis) => vis.funcName === specialVisualisation)
}) ?? false
)
} }
/** /**
@ -30,14 +22,21 @@ export default class ValidationUtils {
public static getSpecialVisualisations( public static getSpecialVisualisations(
renderingConfig: TagRenderingConfigJson renderingConfig: TagRenderingConfigJson
): SpecialVisualization[] { ): SpecialVisualization[] {
return ValidationUtils.getSpecialVisualsationsWithArgs(renderingConfig).map( return ValidationUtils.getSpecialVisualisationsWithArgs(renderingConfig).map(
(spec) => spec["func"] (spec) => spec["func"]
) )
} }
public static getSpecialVisualsationsWithArgs( public static getSpecialVisualisationsWithArgs(
renderingConfig: TagRenderingConfigJson renderingConfig: TagRenderingConfigJson
): RenderingSpecification[] { ): RenderingSpecification[] {
if (!renderingConfig) {
return []
}
const cacheName = "__specialVisualisationsWithArgs_cache"
if (renderingConfig[cacheName]) {
return renderingConfig[cacheName]
}
const translations: any[] = Utils.NoNull([ const translations: any[] = Utils.NoNull([
renderingConfig.render, renderingConfig.render,
...(renderingConfig.mappings ?? []).map((m) => m.then), ...(renderingConfig.mappings ?? []).map((m) => m.then),
@ -59,6 +58,15 @@ export default class ValidationUtils {
all.push(...specials) all.push(...specials)
} }
} }
// _Very_ dirty hack
Object.defineProperty(renderingConfig, cacheName, {
value: all,
enumerable: false,
configurable: true,
writable: true,
})
return all return all
} }
} }

View file

@ -152,8 +152,6 @@ describe("PrepareTheme", () => {
}, },
], ],
startLat: 0, startLat: 0,
pointRendering: null,
lineRendering: null,
startLon: 0, startLon: 0,
startZoom: 0, startZoom: 0,
title: "Test theme", title: "Test theme",