zobrazenie pozicii cisel na mieste kliknutia,

pridane toleracnie 0 pre presny pixel a 1 pre okolie pixela
This commit is contained in:
2026-08-01 19:59:14 +02:00
parent d753697840
commit 47a4c1195f
8 changed files with 219 additions and 51 deletions
+15 -4
View File
@@ -10,7 +10,14 @@ const baseName = () => (imageName.value.replace(/\.[^.]+$/, '').replace(/[^\p{L}
async function exportPng(): Promise<void> {
if (!imageCanvas.value) return store.notify('Najprv načítajte obrázok.', 'warning')
try {
const canvas = renderExportCanvas(imageCanvas.value, { width: imageWidth.value, height: imageHeight.value }, regions.value, groups.value, settings.value.exportOverlay)
const canvas = renderExportCanvas(
imageCanvas.value,
{ width: imageWidth.value, height: imageHeight.value },
regions.value,
groups.value,
settings.value.exportOverlay,
settings.value.useUserClickPositionForLabels,
)
downloadBlob(await canvasToBlob(canvas), `${baseName()}-ocislovane.png`)
} catch (error) { store.notify(error instanceof Error ? error.message : 'Export PNG zlyhal.', 'error') }
}
@@ -22,9 +29,13 @@ function exportCsv(): void {
async function exportJson(): Promise<void> {
if (!imageWidth.value) return store.notify('Nie je čo exportovať.', 'warning')
const project = await store.serializeProject(true)
downloadBlob(new Blob([JSON.stringify(project)], { type: 'application/json' }), `${baseName()}-projekt.json`)
if (!project.imageDataUrl) store.notify('Obrázok je väčší než 5 MB. Pri importe JSON ho bude potrebné znova vybrať.', 'info')
try {
const project = await store.serializeProject(true)
downloadBlob(new Blob([JSON.stringify(project)], { type: 'application/json' }), `${baseName()}-projekt.json`)
if (!project.imageDataUrl) store.notify('Obrázok je väčší než 5 MB. Pri importe JSON ho bude potrebné znova vybrať.', 'info')
} catch (error) {
store.notify(error instanceof Error ? error.message : 'Export projektu JSON zlyhal.', 'error')
}
}
</script>
+12 -4
View File
@@ -3,7 +3,7 @@ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useMosaicStore } from '@/stores/mosaic'
import { rgbToHex, relativeLuminance } from '@/utils/colors'
import { forEachMaskRun } from '@/utils/regionMask'
import { forEachMaskRun, resolveRegionLabelPosition } from '@/utils/regionMask'
import { useCanvasViewport } from '@/composables/useCanvasViewport'
import ImageDropzone from './ImageDropzone.vue'
@@ -76,8 +76,9 @@ function draw(): void {
const group = groupMap.get(region.groupId)
if (!group) continue
if (selectedGroupId.value && settings.value.otherGroupsMode === 'hidden' && group.id !== selectedGroupId.value) continue
const x = viewport.x + region.labelPosition.x * viewport.scale
const y = viewport.y + region.labelPosition.y * viewport.scale
const labelPosition = resolveRegionLabelPosition(region, settings.value.useUserClickPositionForLabels)
const x = viewport.x + labelPosition.x * viewport.scale
const y = viewport.y + labelPosition.y * viewport.scale
const active = region.id === selectedRegionId.value
context.beginPath()
context.arc(x, y, active ? 13 : 11, 0, Math.PI * 2)
@@ -133,7 +134,14 @@ function wheel(event: WheelEvent): void {
}
watch([imageCanvas, imageWidth, imageHeight], async () => { await nextTick(); resetViewport() })
watch([regions, groups, selectedGroupId, selectedRegionId, () => settings.value.otherGroupsMode], draw, { deep: true })
watch([
regions,
groups,
selectedGroupId,
selectedRegionId,
() => settings.value.otherGroupsMode,
() => settings.value.useUserClickPositionForLabels,
], draw, { deep: true })
onMounted(() => {
observer = new ResizeObserver(resize)
+6 -1
View File
@@ -15,7 +15,7 @@ function updateColorTolerance(event: Event): void {
<h2>Nastavenia analýzy</h2>
<label class="range-field">
<span><b>Tolerancia výberu</b><output>{{ settings.floodTolerance }}</output></span>
<input v-model.number="settings.floodTolerance" type="range" min="2" max="100" step="1" />
<input v-model.number="settings.floodTolerance" type="range" min="0" max="100" step="1" />
<small>Vyššia hodnota spojí väčšie farebné odchýlky v jednej ploche.</small>
</label>
<label class="range-field">
@@ -46,6 +46,10 @@ function updateColorTolerance(event: Event): void {
<span><b>Prahová tmavosť obrysu</b><output>{{ settings.darkBoundaryThreshold.toFixed(3) }}</output></span>
<input v-model.number="settings.darkBoundaryThreshold" type="range" min="0" max="0.2" step="0.005" />
</label>
<label class="check-field">
<input v-model="settings.useUserClickPositionForLabels" type="checkbox" />
<span>Zobrazovať čísla na pozícii kliknutia</span>
</label>
</details>
</section>
</template>
@@ -62,4 +66,5 @@ select { width: 100%; }
details { margin-top: 13px; color: #475569; font-size: .76rem; }
summary { cursor: pointer; }
.compact { margin-bottom: 4px; }
.check-field { display: flex; align-items: center; gap: 7px; margin-top: 12px; color: #475569; cursor: pointer; }
</style>