feat: areas of documents
This commit is contained in:
parent
9ced4f6f29
commit
09f3466073
Binary file not shown.
@ -6,4 +6,18 @@ type Entity struct {
|
||||
Name string
|
||||
Path string
|
||||
ProjectId string
|
||||
Areas []Area
|
||||
}
|
||||
|
||||
type Area struct {
|
||||
Id string
|
||||
Name string
|
||||
StartX int
|
||||
StartY int
|
||||
EndX int
|
||||
EndY int
|
||||
}
|
||||
|
||||
func (e *Entity) AddArea(a Area) {
|
||||
e.Areas = append(e.Areas, a)
|
||||
}
|
||||
|
||||
@ -19,12 +19,12 @@ func (collection *DocumentCollection) AddDocument(document Entity) {
|
||||
collection.Documents = append(collection.Documents, document)
|
||||
}
|
||||
|
||||
func (collection *DocumentCollection) GetDocumentById(id string) Entity {
|
||||
var foundDocument Entity
|
||||
func (collection *DocumentCollection) GetDocumentById(id string) *Entity {
|
||||
var foundDocument *Entity
|
||||
|
||||
for _, d := range collection.Documents {
|
||||
for index, d := range collection.Documents {
|
||||
if d.Id == id {
|
||||
foundDocument = d
|
||||
foundDocument = &collection.Documents[index]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from "react"
|
||||
import React, { useEffect, useRef, useState } from "react"
|
||||
import { useProject } from "../../context/Project/provider"
|
||||
import { ipc } from "../../wailsjs/wailsjs/go/models"
|
||||
|
||||
|
||||
const loadImage = (path: string): Promise<HTMLImageElement> => {
|
||||
@ -14,31 +15,171 @@ const loadImage = (path: string): Promise<HTMLImageElement> => {
|
||||
}
|
||||
|
||||
const DocumentRenderer = () => {
|
||||
const { getSelectedDocument } = useProject()
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const { getSelectedDocument, requestAddArea } = useProject()
|
||||
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 })
|
||||
const selectedDocument = getSelectedDocument()
|
||||
const areas = selectedDocument?.areas
|
||||
const documentCanvas = useRef<HTMLCanvasElement>(null)
|
||||
const areaCanvas = useRef<HTMLCanvasElement>(null)
|
||||
const drawingCanvas = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
// console.log('selectedDocument: ', selectedDocument)
|
||||
|
||||
let downClickX = 0
|
||||
let downClickY = 0
|
||||
let isMouseDown = false
|
||||
|
||||
const applyCanvasSizes = (size: { width: number, height: number }) => {
|
||||
const documentCanvasInstance = documentCanvas.current
|
||||
if (!documentCanvasInstance) return
|
||||
documentCanvasInstance.width = size.width
|
||||
documentCanvasInstance.height = size.height
|
||||
|
||||
const areaCanvasInstance = areaCanvas.current
|
||||
if (!areaCanvasInstance) return
|
||||
areaCanvasInstance.width = size.width
|
||||
areaCanvasInstance.height = size.height
|
||||
|
||||
const drawingCanvasInstance = drawingCanvas.current
|
||||
if (!drawingCanvasInstance) return
|
||||
drawingCanvasInstance.width = size.width
|
||||
drawingCanvasInstance.height = size.height
|
||||
}
|
||||
|
||||
const applyDocumentToCanvas = async (path: string) => {
|
||||
const image = await loadImage(path)
|
||||
applyCanvasSizes({ width: image.naturalWidth, height: image.naturalHeight })
|
||||
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
canvas.width = image.naturalWidth
|
||||
canvas.height = image.naturalHeight
|
||||
const documentCanvasInstance = documentCanvas.current
|
||||
if (!documentCanvasInstance) return
|
||||
|
||||
const context = canvas.getContext('2d')
|
||||
const context = documentCanvasInstance.getContext('2d')
|
||||
if (!context) return
|
||||
context.drawImage(image, 0, 0, image.width, image.height)
|
||||
|
||||
if (areas) applyAreasToCanvas()
|
||||
}
|
||||
|
||||
const applyAreasToCanvas = () => {
|
||||
if (!areas || !areas.length) return
|
||||
const areaCanvasInstance = areaCanvas.current
|
||||
if (!areaCanvasInstance) return
|
||||
const context = areaCanvasInstance.getContext("2d")
|
||||
if (!context) return
|
||||
|
||||
context.clearRect(0, 0, areaCanvasInstance.width, areaCanvasInstance.height)
|
||||
|
||||
areas.forEach(a => {
|
||||
console.log('area: ', a)
|
||||
const width = a.endX - a.startX
|
||||
const height = a.endY - a.startY
|
||||
const x = a.startX
|
||||
const y = a.startY
|
||||
console.log(`context.rect: `, x, y, width, height)
|
||||
context.rect(x, y, width, height)
|
||||
context.lineWidth = 2
|
||||
context.strokeStyle = '#dc8dec'
|
||||
context.stroke()
|
||||
})
|
||||
}
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
const drawingCanvasInstance = drawingCanvas.current
|
||||
if (!drawingCanvasInstance) return
|
||||
|
||||
downClickX = e.nativeEvent.offsetX
|
||||
downClickY = e.nativeEvent.offsetY
|
||||
isMouseDown = true
|
||||
}
|
||||
|
||||
const handleMouseUp = async (e: React.MouseEvent) => {
|
||||
const drawingCanvasInstance = drawingCanvas.current
|
||||
if (!drawingCanvasInstance) return
|
||||
|
||||
const mouseX = e.nativeEvent.offsetX
|
||||
const mouseY = e.nativeEvent.offsetY
|
||||
|
||||
let startX: number, endX: number
|
||||
if (downClickX < mouseX) {
|
||||
startX = downClickX
|
||||
endX = mouseX
|
||||
} else {
|
||||
startX = mouseX
|
||||
endX = downClickX
|
||||
}
|
||||
|
||||
let startY: number, endY: number
|
||||
if (downClickY < mouseY) {
|
||||
startY = downClickY
|
||||
endY = mouseY
|
||||
} else {
|
||||
startY = mouseY
|
||||
endY = downClickY
|
||||
}
|
||||
|
||||
if (selectedDocument?.id)
|
||||
requestAddArea(selectedDocument.id, { startX, startY, endX, endY }).then(response => {
|
||||
console.log('requestAddArea: ', response)
|
||||
}).catch(err => console.log('err requestAddArea :', err))
|
||||
|
||||
const context = drawingCanvasInstance.getContext('2d')
|
||||
context?.clearRect(0, 0, drawingCanvasInstance.width, drawingCanvasInstance.height)
|
||||
isMouseDown = false
|
||||
downClickX = 0
|
||||
downClickY = 0
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
const drawingCanvasInstance = drawingCanvas.current
|
||||
if (!drawingCanvasInstance) return
|
||||
|
||||
let mouseX = e.nativeEvent.offsetX
|
||||
let mouseY = e.nativeEvent.offsetY
|
||||
|
||||
if (isMouseDown) {
|
||||
const context = drawingCanvasInstance.getContext('2d')
|
||||
if (!context) return
|
||||
|
||||
context.clearRect(0, 0, drawingCanvasInstance.width, drawingCanvasInstance.height)
|
||||
context.beginPath()
|
||||
const width = mouseX - downClickX
|
||||
const height = mouseY - downClickY
|
||||
context.rect(downClickX, downClickY, width, height)
|
||||
context.strokeStyle = '#000'
|
||||
context.lineWidth = 2
|
||||
context.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const selectedDocument = getSelectedDocument()
|
||||
const documentPath = selectedDocument?.path
|
||||
if (documentPath) applyDocumentToCanvas(selectedDocument.path)
|
||||
}, [getSelectedDocument])
|
||||
if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path)
|
||||
}, [selectedDocument?.id])
|
||||
|
||||
return (
|
||||
<canvas ref={canvasRef}></canvas>
|
||||
)
|
||||
useEffect(() => {
|
||||
applyAreasToCanvas()
|
||||
}, [areas, areas?.length])
|
||||
|
||||
return <div className="relative">
|
||||
<canvas
|
||||
className="absolute"
|
||||
ref={documentCanvas}
|
||||
/>
|
||||
<canvas
|
||||
className="absolute"
|
||||
ref={areaCanvas}
|
||||
// width={canvasSize.width}
|
||||
// height={canvasSize.height}
|
||||
/>
|
||||
<canvas
|
||||
className="absolute"
|
||||
ref={drawingCanvas}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseMove={handleMouseMove}
|
||||
// width={canvasSize.width}
|
||||
// height={canvasSize.height}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default DocumentRenderer
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { createContext, ReactNode, useContext, useState } from 'react'
|
||||
import { GetDocuments, RequestAddDocument, RequestAddDocumentGroup } from '../../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import { GetDocuments, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup } from '../../wailsjs/wailsjs/go/ipc/Channel'
|
||||
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
||||
import { ProjectContextType, ProjectProps } from './types'
|
||||
import { AddAreaProps, ProjectContextType, ProjectProps } from './types'
|
||||
import makeDefaultProject from './makeDefaultProject'
|
||||
import { LogPrint } from '../../wailsjs/wailsjs/runtime/runtime'
|
||||
|
||||
const ProjectContext = createContext<ProjectContextType>(makeDefaultProject())
|
||||
|
||||
@ -18,33 +19,41 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
||||
|
||||
const updateDocuments = async () => {
|
||||
GetDocuments().then(response => {
|
||||
setDocuments(response.documents)
|
||||
setGroups(response.groups)
|
||||
if (response.documents.length) setDocuments(response.documents)
|
||||
if (response.groups.length) setGroups(response.groups)
|
||||
Promise.resolve(response)
|
||||
})
|
||||
}
|
||||
|
||||
const requestAddDocument = async (groupId: string, documentName: string) => {
|
||||
const response = await RequestAddDocument(groupId, documentName)
|
||||
if (response.id) updateDocuments()
|
||||
if (response.id) await updateDocuments()
|
||||
return response
|
||||
}
|
||||
|
||||
const requestAddDocumentGroup = async (groupName: string) => {
|
||||
const response = await RequestAddDocumentGroup(groupName)
|
||||
if (response.id) updateDocuments()
|
||||
if (response.id) await updateDocuments()
|
||||
return response
|
||||
}
|
||||
|
||||
const requestAddArea = async (documentId: string, area: AddAreaProps): Promise<ipc.Area> => {
|
||||
const response = await RequestAddArea(documentId, new ipc.Area(area))
|
||||
|
||||
if (response.id) await updateDocuments()
|
||||
return response
|
||||
}
|
||||
|
||||
const getSelectedDocument = () => documents.find(d => d.id === selectedDocumentId)
|
||||
|
||||
if (!documents.length || !groups.length) updateDocuments()
|
||||
if (!documents.length && !groups.length) updateDocuments()
|
||||
|
||||
const value = {
|
||||
id: '',
|
||||
documents,
|
||||
groups,
|
||||
getSelectedDocument,
|
||||
requestAddArea,
|
||||
requestAddDocument,
|
||||
requestAddDocumentGroup,
|
||||
selectedDocumentId,
|
||||
|
||||
@ -6,10 +6,19 @@ export type ProjectProps = {
|
||||
groups: ipc.Group[],
|
||||
}
|
||||
|
||||
export type AddAreaProps = {
|
||||
name?: string,
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number
|
||||
}
|
||||
|
||||
export type ProjectContextType = {
|
||||
getSelectedDocument: () => ipc.Document | undefined
|
||||
requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>,
|
||||
requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>,
|
||||
selectedDocumentId: string,
|
||||
setSelectedDocumentId: (id: string) => void,
|
||||
requestAddArea: (documentId: string, area: AddAreaProps) => Promise<ipc.Area>
|
||||
requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>
|
||||
requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>
|
||||
selectedDocumentId: string
|
||||
setSelectedDocumentId: (id: string) => void
|
||||
} & ProjectProps
|
||||
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -4,6 +4,8 @@ import {ipc} from '../models';
|
||||
|
||||
export function GetDocuments():Promise<ipc.GetDocumentsResponse>;
|
||||
|
||||
export function RequestAddArea(arg1:string,arg2:ipc.Area):Promise<ipc.Area>;
|
||||
|
||||
export function RequestAddDocument(arg1:string,arg2:string):Promise<ipc.Document>;
|
||||
|
||||
export function RequestAddDocumentGroup(arg1:string):Promise<ipc.Group>;
|
||||
|
||||
@ -6,6 +6,10 @@ export function GetDocuments() {
|
||||
return window['go']['ipc']['Channel']['GetDocuments']();
|
||||
}
|
||||
|
||||
export function RequestAddArea(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestAddArea'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function RequestAddDocument(arg1, arg2) {
|
||||
return window['go']['ipc']['Channel']['RequestAddDocument'](arg1, arg2);
|
||||
}
|
||||
|
||||
@ -1,11 +1,34 @@
|
||||
export namespace ipc {
|
||||
|
||||
export class Area {
|
||||
id: string;
|
||||
name: string;
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Area(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.id = source["id"];
|
||||
this.name = source["name"];
|
||||
this.startX = source["startX"];
|
||||
this.startY = source["startY"];
|
||||
this.endX = source["endX"];
|
||||
this.endY = source["endY"];
|
||||
}
|
||||
}
|
||||
export class Document {
|
||||
id: string;
|
||||
groupId: string;
|
||||
name: string;
|
||||
path: string;
|
||||
projectId: string;
|
||||
areas: Area[];
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Document(source);
|
||||
@ -18,7 +41,26 @@ export namespace ipc {
|
||||
this.name = source["name"];
|
||||
this.path = source["path"];
|
||||
this.projectId = source["projectId"];
|
||||
this.areas = this.convertValues(source["areas"], Area);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class Group {
|
||||
id: string;
|
||||
|
||||
@ -23,12 +23,25 @@ func (c *Channel) GetDocuments() GetDocumentsResponse {
|
||||
}
|
||||
|
||||
for _, d := range documents {
|
||||
jsonAreas := make([]Area, 0)
|
||||
for _, a := range d.Areas {
|
||||
jsonAreas = append(jsonAreas, Area{
|
||||
Id: a.Id,
|
||||
Name: a.Name,
|
||||
StartX: a.StartX,
|
||||
StartY: a.StartY,
|
||||
EndX: a.EndX,
|
||||
EndY: a.EndY,
|
||||
})
|
||||
}
|
||||
|
||||
jsonDocument := Document{
|
||||
Id: d.Id,
|
||||
GroupId: d.GroupId,
|
||||
Name: d.Name,
|
||||
Path: d.Path,
|
||||
ProjectId: d.ProjectId,
|
||||
Areas: jsonAreas,
|
||||
}
|
||||
response.Documents = append(response.Documents, jsonDocument)
|
||||
}
|
||||
@ -87,7 +100,7 @@ func (c *Channel) RequestAddDocumentGroup(name string) Group {
|
||||
newGroup := document.Group{
|
||||
Id: uuid.NewString(),
|
||||
Name: name,
|
||||
ProjectId: "something else",
|
||||
ProjectId: "something else", // TODO: change me
|
||||
}
|
||||
|
||||
document.GetGroupCollection().AddDocumentGroup(newGroup)
|
||||
@ -101,3 +114,26 @@ func (c *Channel) RequestAddDocumentGroup(name string) Group {
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (c *Channel) RequestAddArea(documentId string, area Area) Area {
|
||||
runtime.LogDebug(app.GetInstance().Context, "RequestAddArea in go")
|
||||
foundDocument := document.GetDocumentCollection().GetDocumentById(documentId)
|
||||
|
||||
var id string
|
||||
if area.Id == "" {
|
||||
id = uuid.NewString()
|
||||
} else {
|
||||
id = area.Id
|
||||
}
|
||||
|
||||
newArea := document.Area{
|
||||
Id: id,
|
||||
Name: area.Name,
|
||||
StartX: area.StartX,
|
||||
EndX: area.EndX,
|
||||
StartY: area.StartY,
|
||||
EndY: area.EndY,
|
||||
}
|
||||
foundDocument.AddArea(newArea)
|
||||
return Area(newArea)
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ type Document struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
ProjectId string `json:"projectId"`
|
||||
Areas []Area `json:"areas"`
|
||||
}
|
||||
|
||||
type DocumentCollection struct {
|
||||
@ -25,3 +26,12 @@ type GroupCollection struct {
|
||||
Groups []Group `json:"groups"`
|
||||
ProjectId string `json:"projectId"`
|
||||
}
|
||||
|
||||
type Area struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
StartX int `json:"startX"`
|
||||
StartY int `json:"startY"`
|
||||
EndX int `json:"endX"`
|
||||
EndY int `json:"endY"`
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user