diff --git a/core/src/PageMapping.ts b/core/src/PageMapping.ts index a1fcccb..98a0585 100644 --- a/core/src/PageMapping.ts +++ b/core/src/PageMapping.ts @@ -2,7 +2,7 @@ * Holds the information which (zero based) page index maps to a page number. */ export default class PageMapping { - constructor(public pageFactor: number, public detectedOnPage: boolean) {} + constructor(public pageFactor: number = 1, public detectedOnPage: boolean = false) {} /** * Translates a given page index to a page number label as printed on the page. E.g [0,1,2,3,4] could become [I, II, 1, 2]. @@ -10,10 +10,14 @@ export default class PageMapping { */ pageLabel(pageIndex: number) { const pageNumber = pageIndex + this.pageFactor; - if (pageNumber < 0) { - return romanize(pageNumber - this.pageFactor + 1); + if (pageNumber < 1) { + return romanize(Math.abs(pageNumber - this.pageFactor) + 1); } - return `${pageNumber + 1}`; + return `${pageNumber}`; + } + + shifted(): boolean { + return this.pageFactor != 1; } } diff --git a/core/src/transformer/CacluclateStatistics.ts b/core/src/transformer/CacluclateStatistics.ts index dc6ff9d..6c9bf74 100644 --- a/core/src/transformer/CacluclateStatistics.ts +++ b/core/src/transformer/CacluclateStatistics.ts @@ -73,7 +73,6 @@ export default class CalculateStatistics extends ItemTransformer { const groupedByPage = groupByPage(items); const pageMapping = parsePageMapping(groupedByPage, minX, maxX, minY, maxY); - console.log(pageMapping); // Parse line distances const distanceToOccurrence = {}; @@ -165,7 +164,7 @@ function parsePageMapping( }), { sampleCount: 20, minFulfillment: 0.8 }, ); - return typeof pageFactor === 'undefined' ? new PageMapping(0, false) : new PageMapping(pageFactor, true); + return typeof pageFactor === 'undefined' ? new PageMapping() : new PageMapping(pageFactor, true); } function getMostUsedKey(keyToOccurrence): any { diff --git a/core/src/transformer/Globals.ts b/core/src/transformer/Globals.ts index 22f548f..8ba0a5c 100644 --- a/core/src/transformer/Globals.ts +++ b/core/src/transformer/Globals.ts @@ -25,6 +25,10 @@ export default class Globals { return element; } + getOptional(definition: GlobalDefinition): T | undefined { + return this.map.get(definition.key) as T; + } + set(definition: GlobalDefinition, value: T) { assertNot(this.isDefined(definition), `Global with key '${definition.key}' already registered.`); this.map.set(definition.key, value); diff --git a/core/test/PageMapping.test.ts b/core/test/PageMapping.test.ts index c6b033a..622b1b2 100644 --- a/core/test/PageMapping.test.ts +++ b/core/test/PageMapping.test.ts @@ -1,12 +1,17 @@ import PageMapping from 'src/PageMapping'; test('1-to-1', async () => { - const mapping = new PageMapping(0, false); - expect(mapping.pageFactor).toEqual(0); + const mapping = new PageMapping(1, false); + expect(mapping.pageFactor).toEqual(1); expect([...Array(3).keys()].map((i) => mapping.pageLabel(i))).toEqual(['1', '2', '3']); }); -test('lame start', async () => { - const mapping = new PageMapping(-3, true); +test('one starter page', async () => { + const mapping = new PageMapping(0, true); + expect([...Array(5).keys()].map((i) => mapping.pageLabel(i))).toEqual(['I', '1', '2', '3', '4']); +}); + +test('three starter pages', async () => { + const mapping = new PageMapping(-2, true); expect([...Array(5).keys()].map((i) => mapping.pageLabel(i))).toEqual(['I', 'II', 'III', '1', '2']); }); diff --git a/core/test/transformer/Globals.test.ts b/core/test/transformer/Globals.test.ts index 46be0b2..68050f7 100644 --- a/core/test/transformer/Globals.test.ts +++ b/core/test/transformer/Globals.test.ts @@ -8,6 +8,7 @@ test('not set', async () => { const globals = new Globals(); globals.set(MyGlobalString, '23'); expect(globals.isDefined(MyGlobalNumber)).toBeFalsy(); + expect(globals.getOptional(MyGlobalNumber)).toBeUndefined(); expect(() => globals.get(MyGlobalNumber)).toThrow( `No global with key '${MyGlobalNumber.key}' registered. Only [${MyGlobalString.key}]`, ); @@ -19,6 +20,7 @@ test('set', async () => { expect(globals.isDefined(MyGlobalNumber)).toBeTruthy(); expect(globals.get(MyGlobalNumber)).toEqual(24); + expect(globals.getOptional(MyGlobalNumber)).toEqual(24); expect(globals.keys()).toEqual([MyGlobalNumber.key]); }); diff --git a/examples/Flash-Masques-Temperature/calculateStatistics.json b/examples/Flash-Masques-Temperature/calculateStatistics.json index d1f508a..420fda6 100644 --- a/examples/Flash-Masques-Temperature/calculateStatistics.json +++ b/examples/Flash-Masques-Temperature/calculateStatistics.json @@ -33,7 +33,7 @@ "minY": 36.1763, "maxY": 811.1348, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/Flash-Masques-Temperature/compactLines.json b/examples/Flash-Masques-Temperature/compactLines.json index 53fb758..143b4df 100644 --- a/examples/Flash-Masques-Temperature/compactLines.json +++ b/examples/Flash-Masques-Temperature/compactLines.json @@ -37,7 +37,7 @@ "minY": 36.1763, "maxY": 811.1348, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/Flash-Masques-Temperature/removeRepetitiveItems.json b/examples/Flash-Masques-Temperature/removeRepetitiveItems.json index 2648987..fca8fb4 100644 --- a/examples/Flash-Masques-Temperature/removeRepetitiveItems.json +++ b/examples/Flash-Masques-Temperature/removeRepetitiveItems.json @@ -36,7 +36,7 @@ "minY": 36.1763, "maxY": 811.1348, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/Flash-Masques-Temperature/sortbyX.json b/examples/Flash-Masques-Temperature/sortbyX.json index a559535..3680966 100644 --- a/examples/Flash-Masques-Temperature/sortbyX.json +++ b/examples/Flash-Masques-Temperature/sortbyX.json @@ -36,7 +36,7 @@ "minY": 36.1763, "maxY": 811.1348, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Art-of-Public-Speaking/calculateStatistics.0.json b/examples/The-Art-of-Public-Speaking/calculateStatistics.0.json index 0122d6c..f058476 100644 --- a/examples/The-Art-of-Public-Speaking/calculateStatistics.0.json +++ b/examples/The-Art-of-Public-Speaking/calculateStatistics.0.json @@ -33,7 +33,7 @@ "minY": 75.60000000000002, "maxY": 712.8, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Art-of-Public-Speaking/compactLines.json b/examples/The-Art-of-Public-Speaking/compactLines.json index 2dc1e43..f62b49e 100644 --- a/examples/The-Art-of-Public-Speaking/compactLines.json +++ b/examples/The-Art-of-Public-Speaking/compactLines.json @@ -37,7 +37,7 @@ "minY": 75.60000000000002, "maxY": 712.8, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Art-of-Public-Speaking/removeRepetitiveItems.json b/examples/The-Art-of-Public-Speaking/removeRepetitiveItems.json index 10a46fa..8cef420 100644 --- a/examples/The-Art-of-Public-Speaking/removeRepetitiveItems.json +++ b/examples/The-Art-of-Public-Speaking/removeRepetitiveItems.json @@ -36,7 +36,7 @@ "minY": 75.60000000000002, "maxY": 712.8, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Art-of-Public-Speaking/sortbyX.json b/examples/The-Art-of-Public-Speaking/sortbyX.json index 1f318ef..fea1aa5 100644 --- a/examples/The-Art-of-Public-Speaking/sortbyX.json +++ b/examples/The-Art-of-Public-Speaking/sortbyX.json @@ -36,7 +36,7 @@ "minY": 75.60000000000002, "maxY": 712.8, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Man-Without-A-Body/calculateStatistics.json b/examples/The-Man-Without-A-Body/calculateStatistics.json index 28fcfd1..8b6d4e2 100644 --- a/examples/The-Man-Without-A-Body/calculateStatistics.json +++ b/examples/The-Man-Without-A-Body/calculateStatistics.json @@ -33,7 +33,7 @@ "minY": 75.025, "maxY": 747.22, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Man-Without-A-Body/compactLines.json b/examples/The-Man-Without-A-Body/compactLines.json index 28a0155..e57bdb2 100644 --- a/examples/The-Man-Without-A-Body/compactLines.json +++ b/examples/The-Man-Without-A-Body/compactLines.json @@ -37,7 +37,7 @@ "minY": 75.025, "maxY": 747.22, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Man-Without-A-Body/removeRepetitiveItems.json b/examples/The-Man-Without-A-Body/removeRepetitiveItems.json index 0bf0ec0..19f1c4d 100644 --- a/examples/The-Man-Without-A-Body/removeRepetitiveItems.json +++ b/examples/The-Man-Without-A-Body/removeRepetitiveItems.json @@ -36,7 +36,7 @@ "minY": 75.025, "maxY": 747.22, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/The-Man-Without-A-Body/sortbyX.json b/examples/The-Man-Without-A-Body/sortbyX.json index 0bf0ec0..19f1c4d 100644 --- a/examples/The-Man-Without-A-Body/sortbyX.json +++ b/examples/The-Man-Without-A-Body/sortbyX.json @@ -36,7 +36,7 @@ "minY": 75.025, "maxY": 747.22, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/examples/compressed.tracemonkey-pldi-09/removeRepetitiveItems.json b/examples/compressed.tracemonkey-pldi-09/removeRepetitiveItems.json index 596dd7a..9ca213e 100644 --- a/examples/compressed.tracemonkey-pldi-09/removeRepetitiveItems.json +++ b/examples/compressed.tracemonkey-pldi-09/removeRepetitiveItems.json @@ -36,7 +36,7 @@ "minY": 68.44329999999982, "maxY": 713.7734000000003, "pageMapping": { - "pageFactor": 0, + "pageFactor": 1, "detectedOnPage": false } } diff --git a/ui/src/debug/CurrentPage.svelte b/ui/src/debug/CurrentPage.svelte index 6d1dd69..a3e2be3 100644 --- a/ui/src/debug/CurrentPage.svelte +++ b/ui/src/debug/CurrentPage.svelte @@ -11,7 +11,7 @@ export let pageControl: PageControl; export let pageIndex: number; - let { pagePinned, canPrev, canNext } = pageControl; + let { pagePinned, canPrev, canNext, pageMapping } = pageControl;
@@ -20,7 +20,13 @@ {/if} -
Page {pageIndex + 1} {$pagePinned ? '' : ' / ' + pageControl.totalPages}
+
+ Page + {$pageMapping.pageLabel(pageIndex)} + {#if $pageMapping.shifted()} + ({pageIndex + 1}/{pageControl.totalPages}) + {:else}/ {pageControl.totalPages + $pageMapping.pageFactor - 1}{/if} +
{#if $pagePinned}
diff --git a/ui/src/debug/DebugView.svelte b/ui/src/debug/DebugView.svelte index 879705f..aab73b7 100644 --- a/ui/src/debug/DebugView.svelte +++ b/ui/src/debug/DebugView.svelte @@ -3,6 +3,7 @@ import { flip } from 'svelte/animate'; import type Debugger from '@core/Debugger'; + import { PAGE_MAPPING } from '../../../core/src/transformer/CacluclateStatistics'; import { debugStage } from '../config'; import ControlBar from './ControlBar.svelte'; @@ -19,6 +20,7 @@ let onlyRelevantItems = true; $: stageResult = debug.stageResult($debugStage); + $: pageControl.updateMapping(stageResult.globals.getOptional(PAGE_MAPPING)); $: supportsGrouping = !!stageResult.descriptor?.debug?.itemMerger; $: supportsRelevanceFiltering = !stageResult.descriptor?.debug?.showAll; $: visiblePages = pageControl.selectPages(stageResult, onlyRelevantItems, groupingEnabled, $pinnedPageIndex); diff --git a/ui/src/debug/PageControl.ts b/ui/src/debug/PageControl.ts index 059875a..3ded1d6 100644 --- a/ui/src/debug/PageControl.ts +++ b/ui/src/debug/PageControl.ts @@ -1,5 +1,5 @@ -import Page from '@core/debug/Page'; import StageResult from '@core/debug/StageResult'; +import PageMapping from '../../../core/src/PageMapping'; import { Writable, writable, get, Readable, derived } from 'svelte/store'; export default class PageControl { @@ -8,6 +8,7 @@ export default class PageControl { canPrev: Readable; canNext: Readable; pagesWithItems: Set = new Set(); + pageMapping: Writable = writable(new PageMapping()); constructor(public totalPages: number) { this.pinnedPageIndex = writable(undefined); @@ -22,6 +23,12 @@ export default class PageControl { this.unpinPage.bind(this); } + updateMapping(pageMapping: PageMapping | undefined) { + if (pageMapping) { + this.pageMapping.set(pageMapping); + } + } + selectPages(stageResult: StageResult, relevantChangesOnly: boolean, groupItems: boolean, pinnedPage?: number) { const allRelevantPages = stageResult.selectPages(relevantChangesOnly, groupItems); this.pagesWithItems = new Set( diff --git a/ui/src/debug/PageSelectionPopup.svelte b/ui/src/debug/PageSelectionPopup.svelte index 6bd85bb..39fcac4 100644 --- a/ui/src/debug/PageSelectionPopup.svelte +++ b/ui/src/debug/PageSelectionPopup.svelte @@ -8,7 +8,8 @@ export let pageControl: PageControl; - let { pinnedPageIndex, pagePinned } = pageControl; + let { pinnedPageIndex, pagePinned, pageMapping } = pageControl; + let showIndex = false; const popupOpened: Writable = getContext('popupOpened'); @@ -34,7 +35,7 @@
pageControl.pageHasItems(idx) && pinPage(idx)} class="px-2 border border-gray-300 rounded-full text-center {pageControl.pageHasItems(idx) ? ($pinnedPageIndex === idx ? 'bg-select' : 'hover:text-select hover:border-select cursor-pointer') : 'opacity-50'}"> - {idx + 1} + {#if showIndex}{idx}{:else}{$pageMapping.pageLabel(idx)}{/if}
{/each}