feat: areas of documents

This commit is contained in:
Joshua Shoemaker 2022-12-14 23:39:04 -06:00
parent 9ced4f6f29
commit 09f3466073
11 changed files with 298 additions and 31 deletions

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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>;

View File

@ -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);
}

View File

@ -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;

View File

@ -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)
}

View File

@ -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"`
}