Skip to content

Commit

Permalink
Implement closest region check in API, replaces slow frontend impl
Browse files Browse the repository at this point in the history
  • Loading branch information
SmilyOrg committed Oct 8, 2024
1 parent be34883 commit c0a562e
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 22 deletions.
9 changes: 9 additions & 0 deletions api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,15 @@ paths:
type: number
example: 200

- name: closest
in: query
description: |
If true, return the closest region to the specified `x` and `y` coordinates.
The `w` and `h` parameters are ignored in this case.
schema:
type: boolean
example: false

- name: limit
in: query
schema:
Expand Down
8 changes: 8 additions & 0 deletions internal/layout/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ func (regionSource PhotoRegionSource) GetRegionById(id int, scene *render.Scene,
return regionSource.getRegionFromPhoto(id, &photo, scene, regionConfig)
}

func (regionSource PhotoRegionSource) GetRegionClosestTo(p render.Point, scene *render.Scene, regionConfig render.RegionConfig) (region render.Region, ok bool) {
photo, ok := scene.GetClosestPhotoRef(p)
if !ok {
return render.Region{}, false
}
return regionSource.getRegionFromPhoto(1+photo.Index, photo.Photo, scene, regionConfig), true
}

func layoutFitRow(row []render.Photo, bounds render.Rect, imageSpacing float64) float64 {
count := len(row)
if count == 0 {
Expand Down
17 changes: 16 additions & 1 deletion internal/openapi/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions internal/render/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type RegionSource interface {
GetRegionsFromImageId(image.ImageId, *Scene, RegionConfig) []Region
GetRegionChanFromBounds(Rect, *Scene, RegionConfig) <-chan Region
GetRegionById(int, *Scene, RegionConfig) Region
GetRegionClosestTo(Point, *Scene, RegionConfig) (region Region, ok bool)
}

type SceneId = string
Expand Down Expand Up @@ -267,6 +268,28 @@ func (scene *Scene) GetVisiblePhotoRefs(view Rect, maxCount int) <-chan PhotoRef
return out
}

func (s *Scene) GetClosestPhotoRef(p Point) (ref PhotoRef, ok bool) {
minIndex := -1
minDistSq := math.MaxFloat64
for i := range s.Photos {
photo := &s.Photos[i]
dx := photo.Sprite.Rect.X - p.X
dy := photo.Sprite.Rect.Y - p.Y
distSq := dx*dx + dy*dy
if distSq < minDistSq {
minDistSq = distSq
minIndex = i
}
}
if minIndex == -1 {
return PhotoRef{}, false
}
return PhotoRef{
Index: minIndex,
Photo: &s.Photos[minIndex],
}, true
}

func (scene *Scene) GetVisiblePhotos(view Rect) <-chan Photo {
out := make(chan Photo, 100)
go func() {
Expand Down Expand Up @@ -316,6 +339,10 @@ func (scene *Scene) GetRegionsByImageId(id image.ImageId, limit int) []Region {
return scene.RegionSource.GetRegionsFromImageId(id, scene, query)
}

func (scene *Scene) GetRegionClosestTo(p Point) (region Region, ok bool) {
return scene.RegionSource.GetRegionClosestTo(p, scene, RegionConfig{})
}

func (scene *Scene) GetRegionChan(bounds Rect) <-chan Region {
if scene.RegionSource == nil {
return nil
Expand Down
21 changes: 21 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,27 @@ func (*Api) GetScenesSceneIdRegions(w http.ResponseWriter, r *http.Request, scen
return
}
regions = scene.GetRegionsByImageId(image.ImageId(*params.FileId), limit)
} else if params.Closest != nil && *params.Closest {
if params.X == nil || params.Y == nil {
problem(w, r, http.StatusBadRequest, "x and y required")
return
}
if params.Limit == nil || *params.Limit != 1 {
problem(w, r, http.StatusBadRequest, "limit must be set to 1")
return
}

p := render.Point{
X: float64(*params.X),
Y: float64(*params.Y),
}

region, ok := scene.GetRegionClosestTo(p)
if !ok {
regions = []render.Region{}
} else {
regions = []render.Region{region}
}
} else {
if params.X == nil || params.Y == nil || params.W == nil || params.H == nil {
problem(w, r, http.StatusBadRequest, "bounds or file_id required")
Expand Down
27 changes: 7 additions & 20 deletions ui/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ export async function getRegionsWithFileId(sceneId, id) {
return response.items;
}

export async function getRegionClosestTo(sceneId, x, y) {
if (!sceneId) return null;
const response = await get(`/scenes/${sceneId}/regions?x=${x}&y=${y}&closest=true&limit=1`);
if (!response.items?.length) return null;
return response.items[0];
}

export async function getRegion(sceneId, id) {
return get(`/scenes/${sceneId}/regions/${id}`);
}
Expand All @@ -105,26 +112,6 @@ export async function getCenterRegion(sceneId, x, y, w, h) {
return minRegion;
}

export async function getRegionClosestTo(sceneId, x, y, w, h, rx, ry) {
const regions = await getRegions(sceneId, x, y, w, h);
if (!regions) return null;
let minDistSq = Infinity;
let minRegion = null;
for (let i = 0; i < regions.length; i++) {
const region = regions[i];
const rcx = region.bounds.x + region.bounds.w*0.5;
const rcy = region.bounds.y + region.bounds.h*0.5;
const dx = rcx - rx;
const dy = rcy - ry;
const distSq = dx*dx + dy*dy;
if (distSq < minDistSq) {
minDistSq = distSq;
minRegion = region;
}
}
return minRegion;
}

export async function getCollections() {
return get(`/collections`);
}
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/ScrollViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,6 @@ watchDebounced(scrollY, async (sy) => {
const { x, y, w, h } = view.value;
const center = await getRegionClosestTo(
scene.value.id,
x, y, w, h,
x, y + h * focusScreenRatioY,
);
const fileId = center?.data?.id;
Expand Down

0 comments on commit c0a562e

Please sign in to comment.