Refactor Context Groups & Area Detection #4
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -3,10 +3,12 @@
|
||||
"*.css": "tailwindcss"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"consts",
|
||||
"headlessui",
|
||||
"heroicons",
|
||||
"konva",
|
||||
"libretranslate",
|
||||
"tailwindcss",
|
||||
"Tesseract",
|
||||
"Textualize",
|
||||
"wailsjs"
|
||||
|
||||
@ -32,6 +32,41 @@ func SetContextGroupCollectionBySerialized(serialized []entities.SerializedLinke
|
||||
return &newInstance
|
||||
}
|
||||
|
||||
func (collection *ContextGroupCollection) DoesGroupExistBetweenProcessedAreas(ancestorAreaId string, descendantAreaId string) bool {
|
||||
ancestorGroup, _ := collection.FindGroupByLinkedProcessedAreaId(ancestorAreaId)
|
||||
descendantGroup, _ := collection.FindGroupByLinkedProcessedAreaId(descendantAreaId)
|
||||
|
||||
isAncestorInAnyInGroup := ancestorGroup != nil
|
||||
isDescendantInAnyInGroup := descendantGroup != nil
|
||||
areBothInAnyInGroup := isAncestorInAnyInGroup && isDescendantInAnyInGroup
|
||||
areBothInSameGroup := false
|
||||
if areBothInAnyInGroup {
|
||||
areBothInSameGroup = ancestorGroup.Id == descendantGroup.Id
|
||||
}
|
||||
|
||||
return areBothInSameGroup
|
||||
}
|
||||
|
||||
func (collection *ContextGroupCollection) DisconnectProcessedAreas(ancestorAreaId string, descendantAreaId string) bool {
|
||||
doesConnectionExist := collection.DoesGroupExistBetweenProcessedAreas(ancestorAreaId, descendantAreaId)
|
||||
|
||||
if !doesConnectionExist {
|
||||
return false
|
||||
}
|
||||
|
||||
ancestorGroup, _ := collection.FindGroupByLinkedProcessedAreaId(ancestorAreaId)
|
||||
|
||||
wasRemoved := false
|
||||
for i, group := range collection.Groups {
|
||||
if group.Id == ancestorGroup.Id {
|
||||
collection.Groups = append(collection.Groups[:i], collection.Groups[i+1:]...)
|
||||
wasRemoved = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return wasRemoved
|
||||
}
|
||||
|
||||
func (collection *ContextGroupCollection) FindGroupById(id string) (*entities.LinkedAreaList, error) {
|
||||
found := false
|
||||
var foundGroup *entities.LinkedAreaList = nil
|
||||
|
||||
@ -12,39 +12,22 @@ import { useStage } from './context/provider'
|
||||
type Props = {
|
||||
isActive: boolean,
|
||||
area: entities.Area,
|
||||
setHoveredOverAreaIds: Function
|
||||
setHoveredProcessedArea: Function
|
||||
}
|
||||
|
||||
type coordinates = { x: number, y: number }
|
||||
|
||||
const Area = (props: Props) => {
|
||||
const { getProcessedAreaById, selectedAreaId, setSelectedAreaId } = useProject()
|
||||
const { selectedAreaId, setSelectedAreaId } = useProject()
|
||||
const { scale } = useStage()
|
||||
const shapeRef = React.useRef<Konva.Rect>(null)
|
||||
const [isAreaContextMenuOpen, setIsAreaContextMenuOpen] = useState(false)
|
||||
const [areaContextMenuPosition, setAreaContextMenuPosition] = useState<coordinates>()
|
||||
|
||||
const { area, isActive, setHoveredOverAreaIds, setHoveredProcessedArea } = props
|
||||
const { area, isActive } = props
|
||||
const a = area
|
||||
const width = (a.endX - a.startX)
|
||||
const height = (a.endY - a.startY)
|
||||
|
||||
const handleEnterOrLeave = (e: KonvaEventObject<MouseEvent>) => {
|
||||
const stage = e.currentTarget.getStage()!
|
||||
const currentMousePosition = stage.pointerPos
|
||||
const intersectingNodes = stage.getAllIntersections(currentMousePosition)
|
||||
const drawnAreas = intersectingNodes.filter(n => n.attrs?.isArea)
|
||||
const drawnAreasIds = drawnAreas.map(d => d.attrs?.id)
|
||||
setHoveredOverAreaIds(drawnAreasIds)
|
||||
|
||||
const processedAreaRequests = drawnAreasIds.map(a => getProcessedAreaById(a || ''))
|
||||
Promise.all(processedAreaRequests).then(responses => {
|
||||
const validResponses = responses.filter(r => r?.id) as entities.ProcessedArea[]
|
||||
setHoveredProcessedArea(validResponses || [])
|
||||
})
|
||||
}
|
||||
|
||||
const handleContextMenu = (e: KonvaEventObject<PointerEvent>) => {
|
||||
e.evt.preventDefault()
|
||||
const stage = e.currentTarget.getStage()
|
||||
@ -76,8 +59,6 @@ const Area = (props: Props) => {
|
||||
strokeWidth={1}
|
||||
strokeScaleEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
onMouseEnter={handleEnterOrLeave}
|
||||
onMouseLeave={handleEnterOrLeave}
|
||||
onClick={() => handleAreaClick(a.id)}
|
||||
onContextMenu={handleContextMenu}
|
||||
isArea
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Group } from 'react-konva'
|
||||
import { useProject } from '../../context/Project/provider'
|
||||
import { entities } from '../../wailsjs/wailsjs/go/models'
|
||||
@ -12,12 +12,24 @@ import { useStage } from './context/provider'
|
||||
type Props = { scale: number }
|
||||
|
||||
const Areas = ({ scale }: Props) => {
|
||||
const { getSelectedDocument, selectedAreaId } = useProject()
|
||||
const { getSelectedDocument, selectedAreaId, getProcessedAreaById } = useProject()
|
||||
const { isProcessedWordsVisible } = useStage()
|
||||
const areas = getSelectedDocument()?.areas || []
|
||||
const [hoveredOverAreaIds, setHoveredOverAreaIds] = useState<string[]>([])
|
||||
const [hoveredProcessedAreas, setHoveredProcessedArea] = useState<entities.ProcessedArea[]>([])
|
||||
const [editingWord, setEditingWord] = useState<entities.ProcessedWord | null>(null)
|
||||
const [selectedProcessedArea, setSelectedProcessedArea] = useState<entities.ProcessedArea | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAreaId) return setSelectedProcessedArea(null)
|
||||
else {
|
||||
getProcessedAreaById(selectedAreaId).then(res => {
|
||||
if (res) setSelectedProcessedArea(res)
|
||||
}).catch(err => {
|
||||
console.warn('getProcessedAreaById', err)
|
||||
setSelectedProcessedArea(null)
|
||||
})
|
||||
}
|
||||
|
||||
}, [selectedAreaId])
|
||||
|
||||
const renderEditingWord = () => {
|
||||
if (!editingWord) return
|
||||
@ -29,26 +41,20 @@ const Areas = ({ scale }: Props) => {
|
||||
}
|
||||
|
||||
const renderProcessedWords = () => {
|
||||
if (!hoveredProcessedAreas.length) return
|
||||
if (!selectedProcessedArea) return <></>
|
||||
|
||||
return hoveredProcessedAreas.map(a => {
|
||||
const words = a.lines.map(l => l.words).flat()
|
||||
const words = selectedProcessedArea.lines.map(l => l.words).flat()
|
||||
return words.map((w, index) => <ProcessedWord
|
||||
key={index}
|
||||
area={a}
|
||||
area={selectedProcessedArea}
|
||||
word={w}
|
||||
scale={scale}
|
||||
setEditingWord={setEditingWord}
|
||||
/>)
|
||||
})
|
||||
}
|
||||
|
||||
const renderAreas = (areas: entities.Area[]) => areas.map((a, index) => {
|
||||
return <Area key={index}
|
||||
area={a}
|
||||
setHoveredOverAreaIds={setHoveredOverAreaIds}
|
||||
setHoveredProcessedArea={setHoveredProcessedArea}
|
||||
isActive={(hoveredOverAreaIds.includes(a.id) || a.id === selectedAreaId)} />
|
||||
return <Area key={index} area={a} isActive={a.id === selectedAreaId} />
|
||||
})
|
||||
|
||||
return <Group>
|
||||
|
||||
@ -9,15 +9,15 @@ import useImage from 'use-image'
|
||||
import { RectangleCoordinates } from './types'
|
||||
import DrawingArea from './DrawingArea'
|
||||
import getNormalizedRectToBounds from '../../utils/getNormalizedRectToBounds'
|
||||
import processImageArea from '../../useCases/processImageArea'
|
||||
import { useStage } from './context/provider'
|
||||
import ContextConnections from './ContextConnections'
|
||||
import processImageRect from '../../useCases/processImageRect'
|
||||
|
||||
let downClickX: number
|
||||
let downClickY: number
|
||||
|
||||
const CanvasStage = () => {
|
||||
const { getSelectedDocument, requestAddArea, setSelectedAreaId } = useProject()
|
||||
const { getSelectedDocument, updateDocuments, setSelectedAreaId } = useProject()
|
||||
const { scale, scaleStep, maxScale, size, setScale, isAreasVisible, isLinkAreaContextsVisible, isDrawingArea, setIsDrawingArea, startingContextConnection, setStartingContextConnection } = useStage()
|
||||
const [documentImage] = useImage(getSelectedDocument()?.path || '')
|
||||
const documentRef = useRef(null)
|
||||
@ -55,11 +55,12 @@ const CanvasStage = () => {
|
||||
|
||||
const normalizedDrawnRect = getNormalizedRectToBounds(drawingAreaRect, documentWidth, documentHeight, scale)
|
||||
const selectedDocumentId = getSelectedDocument()!.id
|
||||
requestAddArea(selectedDocumentId, normalizedDrawnRect).then(addedArea => {
|
||||
setSelectedAreaId(addedArea.id)
|
||||
processImageArea(selectedDocumentId, addedArea.id)
|
||||
processImageRect(selectedDocumentId, normalizedDrawnRect).then(async addedAreas => {
|
||||
updateDocuments().then(response => {
|
||||
if (!addedAreas.length) return
|
||||
setSelectedAreaId(addedAreas[0].id)
|
||||
})
|
||||
})
|
||||
|
||||
setDrawingAreaRect(null)
|
||||
}
|
||||
|
||||
|
||||
@ -60,6 +60,7 @@ const ConnectionLines = () => {
|
||||
strokeScaleEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
tension={0.2}
|
||||
listening={false}
|
||||
/>
|
||||
})
|
||||
return lines.filter(l => !!l)
|
||||
|
||||
@ -50,6 +50,7 @@ const CurrentDrawingConnection = (props: CurrentDrawingConnectionProps) => {
|
||||
strokeScaleEnabled={false}
|
||||
shadowForStrokeEnabled={false}
|
||||
tension={0.2}
|
||||
listening={false}
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ type Icon = (props: React.SVGProps<SVGSVGElement> & {
|
||||
}) => JSX.Element
|
||||
|
||||
const ToolToggleButton = (props: { icon: Icon, hint: string, isActive: boolean, onClick?: React.MouseEventHandler<HTMLButtonElement> }) => {
|
||||
return <div className="group flex relative pointer-events-auto">
|
||||
return <div className="group flex relative">
|
||||
<button className='pointer-events-auto p-2 bg-white rounded-md block mt-3 shadow-lg hover:bg-slate-100 aria-pressed:bg-indigo-400 aria-pressed:text-white'
|
||||
aria-pressed={props.isActive}
|
||||
onClick={props.onClick}>
|
||||
|
||||
@ -7,10 +7,13 @@ import { entities } from '../../../wailsjs/wailsjs/go/models'
|
||||
import LanguageSelect from '../../utils/LanguageSelect'
|
||||
import { useStage } from '../context/provider'
|
||||
import ToolToggleButton from './ToolToggleButton'
|
||||
import { useNotification } from '../../../context/Notification/provider'
|
||||
import processImageArea from '../../../useCases/processImageArea'
|
||||
|
||||
|
||||
const ToolingOverlay = () => {
|
||||
const { getSelectedDocument, selectedAreaId, } = useProject()
|
||||
const { getSelectedDocument, selectedAreaId, requestUpdateArea, requestUpdateDocument, updateDocuments } = useProject()
|
||||
const { addNotificationToQueue } = useNotification()
|
||||
const {
|
||||
scale, scaleStep, maxScale, setScale,
|
||||
isLinkAreaContextsVisible, setIsLinkAreaContextsVisible,
|
||||
@ -24,7 +27,54 @@ const ToolingOverlay = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedArea(selectedDocument?.areas.find(a => a.id == selectedAreaId))
|
||||
}, [selectedAreaId])
|
||||
}, [selectedAreaId, selectedDocument, selectedArea])
|
||||
|
||||
const handleAreaProcessLanguageSelect = async (selectedLanguage: entities.Language) => {
|
||||
if (!selectedArea) return
|
||||
|
||||
let successfullyUpdatedLanguageOnArea = false
|
||||
try {
|
||||
successfullyUpdatedLanguageOnArea = await requestUpdateArea({ ...selectedArea, ...{ language: selectedLanguage } })
|
||||
} catch (err) {
|
||||
addNotificationToQueue({ message: 'Error updating area language', level: 'error' })
|
||||
return
|
||||
}
|
||||
|
||||
const selectedDocumentId = getSelectedDocument()?.id
|
||||
if (!successfullyUpdatedLanguageOnArea || !selectedDocumentId) {
|
||||
addNotificationToQueue({ message: 'Did not successfully update area language', level: 'warning' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await processImageArea(selectedDocumentId, selectedArea.id)
|
||||
await updateDocuments()
|
||||
addNotificationToQueue({ message: 'Finished processing area', level: 'info' })
|
||||
} catch (err) {
|
||||
addNotificationToQueue({ message: 'Error processing area', level: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleDocumentProcessLanguageSelect = async (selectedLanguage: entities.Language) => {
|
||||
if (!selectedDocument) return
|
||||
|
||||
const currentDocument = selectedDocument
|
||||
currentDocument.defaultLanguage = selectedLanguage
|
||||
await requestUpdateDocument(currentDocument)
|
||||
await updateDocuments()
|
||||
}
|
||||
|
||||
|
||||
const renderLanguageSelect = () => {
|
||||
const defaultLanguage = selectedArea?.language.displayName ? selectedArea?.language : selectedDocument?.defaultLanguage
|
||||
const onSelect = selectedArea ? handleAreaProcessLanguageSelect : handleDocumentProcessLanguageSelect
|
||||
|
||||
return <LanguageSelect
|
||||
styles={{ fontSize: '16px', borderRadius: '2px' }}
|
||||
defaultLanguage={defaultLanguage}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
}
|
||||
|
||||
return <>
|
||||
{/* Top buttons */}
|
||||
@ -36,7 +86,8 @@ const ToolingOverlay = () => {
|
||||
: selectedDocument?.name
|
||||
}
|
||||
</h1>
|
||||
<LanguageSelect styles={{ fontSize: '16px', borderRadius: '2px' }} defaultLanguage={selectedArea?.language || selectedDocument?.defaultLanguage} />
|
||||
{ renderLanguageSelect() }
|
||||
{/* <LanguageSelect styles={{ fontSize: '16px', borderRadius: '2px' }} defaultLanguage={selectedArea?.language.displayName ? selectedArea?.language : selectedDocument?.defaultLanguage} /> */}
|
||||
</div>
|
||||
<div className='flex mt-4 justify-evenly align-top pointer-events-auto'>
|
||||
<MagnifyingGlassMinusIcon className='w-4 h-4' />
|
||||
|
||||
@ -15,13 +15,13 @@ const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: num
|
||||
getAreaById,
|
||||
requestUpdateArea,
|
||||
setSelectedDocumentId,
|
||||
setSelectedAreaId,
|
||||
requestChangeAreaOrder,
|
||||
requestDeleteAreaById,
|
||||
selectedAreaId,
|
||||
setSelectedAreaId,
|
||||
} = useProject()
|
||||
|
||||
const {
|
||||
selectedAreaId,
|
||||
isEditAreaNameInputShowing,
|
||||
setIsEditAreaNameInputShowing,
|
||||
dragOverAreaId,
|
||||
|
||||
7
frontend/consts/index.ts
Normal file
7
frontend/consts/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
const colors = {
|
||||
BRAND_PRIMARY: {
|
||||
hex: '#dc8dec',
|
||||
}
|
||||
}
|
||||
|
||||
export { colors }
|
||||
@ -1,4 +1,4 @@
|
||||
import { entities } from '../../wailsjs/wailsjs/go/models'
|
||||
import { entities, ipc } from '../../wailsjs/wailsjs/go/models'
|
||||
import { ProjectContextType, UserProps } from './types'
|
||||
|
||||
const makeDefaultProject = (): ProjectContextType => ({
|
||||
@ -11,7 +11,7 @@ const makeDefaultProject = (): ProjectContextType => ({
|
||||
getSelectedDocument: () => new entities.Document(),
|
||||
getAreaById: (areaId) => undefined,
|
||||
getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new entities.ProcessedArea()]),
|
||||
requestAddProcessedArea: (processesArea) => Promise.resolve(false),
|
||||
requestAddProcessedArea: (processesArea) => Promise.resolve(new entities.ProcessedArea()),
|
||||
requestAddArea: (documentId, area) => Promise.resolve(new entities.Area()),
|
||||
requestUpdateArea: (updatedArea) => Promise.resolve(false),
|
||||
requestDeleteAreaById: (areaId) => Promise.resolve(false),
|
||||
@ -36,6 +36,7 @@ const makeDefaultProject = (): ProjectContextType => ({
|
||||
requestUpdateProcessedArea: updatedProcessedArea => Promise.resolve(false),
|
||||
requestConnectProcessedAreas: (headId, tailId) => Promise.resolve(false),
|
||||
getSerializedContextGroups: () => Promise.resolve([]),
|
||||
updateDocuments: () => Promise.resolve(new ipc.GetDocumentsResponse())
|
||||
})
|
||||
|
||||
export default makeDefaultProject
|
||||
|
||||
@ -70,6 +70,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
selectedDocumentId,
|
||||
setSelectedDocumentId,
|
||||
currentSession,
|
||||
updateDocuments,
|
||||
...areaMethods,
|
||||
...documentMethods,
|
||||
...sessionMethods,
|
||||
|
||||
@ -41,7 +41,7 @@ export type ProjectContextType = {
|
||||
getSelectedDocument: () => entities.Document | undefined
|
||||
getAreaById: (areaId: string) => entities.Area | undefined
|
||||
getProcessedAreasByDocumentId: (documentId: string) => Promise<entities.ProcessedArea[]>
|
||||
requestAddProcessedArea: (processedArea: entities.ProcessedArea) => Promise<boolean>
|
||||
requestAddProcessedArea: (processedArea: entities.ProcessedArea) => Promise<entities.ProcessedArea>
|
||||
requestAddArea: (documentId: string, area: AddAreaProps) => Promise<entities.Area>
|
||||
requestUpdateArea: (area: AreaProps) => Promise<boolean>
|
||||
requestDeleteAreaById: (areaId: string) => Promise<boolean>
|
||||
@ -68,4 +68,5 @@ export type ProjectContextType = {
|
||||
requestUpdateProcessedArea: (updatedProcessedArea: entities.ProcessedArea) => Promise<boolean>
|
||||
requestConnectProcessedAreas: (headId: string, tailId: string) => Promise<boolean>
|
||||
getSerializedContextGroups: () => Promise<entities.SerializedLinkedProcessedArea[]>
|
||||
updateDocuments: () => Promise<ipc.GetDocumentsResponse>
|
||||
} & ProjectProps
|
||||
@ -16,4 +16,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
colors: {
|
||||
brandPrimary: '#dc8dec',
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { createScheduler, createWorker } from 'tesseract.js'
|
||||
import { createScheduler, createWorker, PSM } from 'tesseract.js'
|
||||
import { GetAreaById, GetDocumentById, GetProcessedAreaById, RequestAddProcessedArea, RequestSaveProcessedTextCollection, RequestUpdateProcessedArea } from '../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import { entities } from '../wailsjs/wailsjs/go/models'
|
||||
import loadImage from './loadImage'
|
||||
@ -79,11 +79,15 @@ const processImageArea = async (documentId: string, areaId: string) => {
|
||||
|
||||
|
||||
const existingProcessedArea = await GetProcessedAreaById(areaId)
|
||||
let didSuccessfullyProcess = false
|
||||
if (existingProcessedArea.id !== areaId) didSuccessfullyProcess = await RequestAddProcessedArea(newProcessedArea)
|
||||
else await RequestUpdateProcessedArea(newProcessedArea)
|
||||
|
||||
saveProcessedText()
|
||||
let didSuccessfullyProcess: boolean // TODO: fix this: this no longer is truthful, returns true or false if there was not a JS error
|
||||
try {
|
||||
if (existingProcessedArea.id !== areaId) await RequestAddProcessedArea(newProcessedArea)
|
||||
else await RequestUpdateProcessedArea(newProcessedArea)
|
||||
saveProcessedText()
|
||||
didSuccessfullyProcess = true
|
||||
} catch (err) {
|
||||
didSuccessfullyProcess = false
|
||||
}
|
||||
return didSuccessfullyProcess
|
||||
}
|
||||
|
||||
|
||||
103
frontend/useCases/processImageRect.ts
Normal file
103
frontend/useCases/processImageRect.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { PSM, createScheduler, createWorker } from 'tesseract.js'
|
||||
import { GetDocumentById, RequestAddArea, RequestAddProcessedArea } from '../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import loadImage from './loadImage'
|
||||
import { entities } from '../wailsjs/wailsjs/go/models'
|
||||
import { saveProcessedText } from './saveData'
|
||||
|
||||
type rect = {
|
||||
startX: number,
|
||||
endX: number,
|
||||
startY: number,
|
||||
endY: number,
|
||||
}
|
||||
|
||||
const processImageRect = async (documentId: string, rectangle: rect): Promise<entities.ProcessedArea[]> => {
|
||||
const foundDocument = await GetDocumentById(documentId)
|
||||
const { path, defaultLanguage } = foundDocument
|
||||
if (!path || !defaultLanguage) return []
|
||||
|
||||
const processLanguage = defaultLanguage.processCode
|
||||
const imageData = await loadImage(path)
|
||||
|
||||
let workerOptions: Partial<Tesseract.WorkerOptions> = {}
|
||||
if (foundDocument.defaultLanguage.isBundledCustom) {
|
||||
workerOptions = {
|
||||
langPath: '/customLanguages',
|
||||
gzip: false,
|
||||
// logger: m => console.log(m)
|
||||
}
|
||||
}
|
||||
|
||||
const worker = await createWorker(workerOptions)
|
||||
await worker.loadLanguage(processLanguage)
|
||||
await worker.initialize(processLanguage)
|
||||
await worker.setParameters({
|
||||
tessedit_pageseg_mode: PSM.AUTO_OSD,
|
||||
})
|
||||
|
||||
const scheduler = createScheduler()
|
||||
scheduler.addWorker(worker)
|
||||
|
||||
const result = await scheduler.addJob('recognize', imageData, {
|
||||
rectangle: {
|
||||
left: rectangle.startX,
|
||||
top: rectangle.startY,
|
||||
width: rectangle.endX - rectangle.startX,
|
||||
height: rectangle.endY - rectangle.startY,
|
||||
}
|
||||
})
|
||||
|
||||
const addAreaRequests = result.data.paragraphs.map(async (p: any) => {
|
||||
const defaultAreaName = p.lines[0]?.words[0]?.text || ''
|
||||
const area = await RequestAddArea(
|
||||
documentId,
|
||||
new entities.Area({
|
||||
name: defaultAreaName,
|
||||
startX: p.bbox.x0,
|
||||
endX: p.bbox.x1,
|
||||
startY: p.bbox.y0,
|
||||
endY: p.bbox.y1,
|
||||
})
|
||||
)
|
||||
|
||||
const processedArea = await RequestAddProcessedArea(new entities.ProcessedArea({
|
||||
id: area.id,
|
||||
documentId,
|
||||
order: area.order,
|
||||
fullText: p.text,
|
||||
lines: p.lines.map((l: any) => new entities.ProcessedLine({
|
||||
fullText: l.text,
|
||||
words: l.words.map((w: any) => new entities.ProcessedWord({
|
||||
areaId: area.id,
|
||||
fullText: w.text,
|
||||
direction: w.direction,
|
||||
confidence: w.confidence,
|
||||
boundingBox: new entities.ProcessedBoundingBox({
|
||||
x0: w.bbox.x0,
|
||||
y0: w.bbox.y0,
|
||||
x1: w.bbox.x1,
|
||||
y1: w.bbox.y1,
|
||||
}),
|
||||
symbols: w.symbols.map((s: any) => new entities.ProcessedSymbol({
|
||||
fullText: s.text,
|
||||
confidence: s.confidence,
|
||||
boundingBox: new entities.ProcessedBoundingBox({
|
||||
x0: s.bbox.x0,
|
||||
y0: s.bbox.y0,
|
||||
x1: s.bbox.x1,
|
||||
y1: s.bbox.y1,
|
||||
})
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
return processedArea
|
||||
})
|
||||
|
||||
const addAreaResponses = await Promise.allSettled(addAreaRequests)
|
||||
const areas = addAreaResponses.filter((val): val is PromiseFulfilledResult<entities.ProcessedArea> => val.status === 'fulfilled').map(val => val.value)
|
||||
await saveProcessedText()
|
||||
return areas
|
||||
}
|
||||
|
||||
export default processImageRect
|
||||
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
4
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -35,7 +35,7 @@ export function RequestAddDocument(arg1:string,arg2:string):Promise<entities.Doc
|
||||
|
||||
export function RequestAddDocumentGroup(arg1:string):Promise<entities.Group>;
|
||||
|
||||
export function RequestAddProcessedArea(arg1:entities.ProcessedArea):Promise<boolean>;
|
||||
export function RequestAddProcessedArea(arg1:entities.ProcessedArea):Promise<entities.ProcessedArea>;
|
||||
|
||||
export function RequestChangeAreaOrder(arg1:string,arg2:number):Promise<entities.Document>;
|
||||
|
||||
@ -53,6 +53,8 @@ export function RequestDeleteDocumentAndChildren(arg1:string):Promise<boolean>;
|
||||
|
||||
export function RequestDeleteProcessedAreaById(arg1:string):Promise<boolean>;
|
||||
|
||||
export function RequestDisconnectProcessedAreas(arg1:string,arg2:string):Promise<boolean>;
|
||||
|
||||
export function RequestSaveContextGroupCollection():Promise<boolean>;
|
||||
|
||||
export function RequestSaveDocumentCollection():Promise<boolean>;
|
||||
|
||||
@ -102,6 +102,10 @@ export function RequestDeleteProcessedAreaById(arg1) {
|
||||
return window['go']['ipc']['Channel']['RequestDeleteProcessedAreaById'](arg1);
|
||||
}
|
||||
|
||||
export function RequestDisconnectProcessedAreas(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestDisconnectProcessedAreas'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function RequestSaveContextGroupCollection() {
|
||||
return window['go']['ipc']['Channel']['RequestSaveContextGroupCollection']();
|
||||
}
|
||||
|
||||
@ -7,17 +7,39 @@ import (
|
||||
"textualize/storage"
|
||||
)
|
||||
|
||||
func (c *Channel) RequestDisconnectProcessedAreas(ancestorAreaId string, descendantAreaId string) bool {
|
||||
contextGroupCollection := contextGroup.GetContextGroupCollection()
|
||||
|
||||
wasSuccessfulDisconnect := contextGroupCollection.DisconnectProcessedAreas(ancestorAreaId, descendantAreaId)
|
||||
if wasSuccessfulDisconnect {
|
||||
wasSuccessfulWrite := c.RequestSaveContextGroupCollection()
|
||||
return wasSuccessfulWrite
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
If a connection already exists, then this method will default to disconnecting the two areas.
|
||||
*/
|
||||
func (c *Channel) RequestConnectProcessedAreas(ancestorAreaId string, descendantAreaId string) bool {
|
||||
contextGroupCollection := contextGroup.GetContextGroupCollection()
|
||||
|
||||
doesContextGroupAlreadyExist := contextGroupCollection.DoesGroupExistBetweenProcessedAreas(ancestorAreaId, descendantAreaId)
|
||||
if doesContextGroupAlreadyExist {
|
||||
return c.RequestDisconnectProcessedAreas(ancestorAreaId, descendantAreaId)
|
||||
}
|
||||
|
||||
processedAreaCollection := document.GetProcessedAreaCollection()
|
||||
|
||||
ancestorArea := processedAreaCollection.GetAreaById(ancestorAreaId)
|
||||
descendantArea := processedAreaCollection.GetAreaById(descendantAreaId)
|
||||
|
||||
wasSuccessfulConnect := contextGroup.GetContextGroupCollection().ConnectProcessedAreas(*ancestorArea, *descendantArea)
|
||||
wasSuccessfulConnect := contextGroupCollection.ConnectProcessedAreas(*ancestorArea, *descendantArea)
|
||||
if wasSuccessfulConnect {
|
||||
wasSuccessfulWrite := c.RequestSaveContextGroupCollection()
|
||||
return wasSuccessfulWrite
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
document "textualize/core/Document"
|
||||
"textualize/entities"
|
||||
@ -35,7 +34,7 @@ func (c *Channel) GetProcessedAreasByDocumentId(id string) []entities.ProcessedA
|
||||
return sortedAreas
|
||||
}
|
||||
|
||||
func (c *Channel) RequestAddProcessedArea(processedArea entities.ProcessedArea) bool {
|
||||
func (c *Channel) RequestAddProcessedArea(processedArea entities.ProcessedArea) entities.ProcessedArea {
|
||||
|
||||
for lineIndex, line := range processedArea.Lines {
|
||||
for wordIndex, word := range line.Words {
|
||||
@ -46,7 +45,7 @@ func (c *Channel) RequestAddProcessedArea(processedArea entities.ProcessedArea)
|
||||
}
|
||||
|
||||
document.GetProcessedAreaCollection().AddProcessedArea(processedArea)
|
||||
return true
|
||||
return *document.GetProcessedAreaCollection().GetAreaById(processedArea.Id)
|
||||
}
|
||||
|
||||
func (c *Channel) RequestDeleteProcessedAreaById(id string) bool {
|
||||
@ -75,9 +74,6 @@ func (c *Channel) RequestDeleteProcessedAreaById(id string) bool {
|
||||
}
|
||||
|
||||
func (c *Channel) RequestUpdateProcessedArea(updatedProcessedArea entities.ProcessedArea) bool {
|
||||
fmt.Println("updatedProcessedArea")
|
||||
fmt.Println(&updatedProcessedArea)
|
||||
fmt.Println()
|
||||
if updatedProcessedArea.Id == "" {
|
||||
return false
|
||||
}
|
||||
@ -87,14 +83,13 @@ func (c *Channel) RequestUpdateProcessedArea(updatedProcessedArea entities.Proce
|
||||
return false
|
||||
}
|
||||
|
||||
successfulAdd := c.RequestAddProcessedArea(updatedProcessedArea)
|
||||
if !successfulAdd {
|
||||
return false
|
||||
}
|
||||
addedProcessedArea := c.RequestAddProcessedArea(updatedProcessedArea)
|
||||
return addedProcessedArea.Id != ""
|
||||
|
||||
fmt.Println("document.GetProcessedAreaCollection().GetAreaById(updatedProcessedArea.Id)")
|
||||
fmt.Println(document.GetProcessedAreaCollection().GetAreaById(updatedProcessedArea.Id))
|
||||
return true
|
||||
// if addedProcessedArea.Id != "" {
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
}
|
||||
|
||||
func (c *Channel) RequestUpdateProcessedWordById(wordId string, newTextValue string) bool {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user