commit 4121779d42ce443ffa8994bce42505428f5a5f46
parent be9932e5679f6e9f285a121b03a2c30db6097f50
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Tue, 8 Oct 2024 15:42:52 -0700
Add hidden margins to aid in selecting objects
Now objects are selected if they're origin is X units from the
pointer, even if they're not that big. This is primarily for touch
devices where it can be difficult to select objects accurately.
Diffstat:
3 files changed, 114 insertions(+), 21 deletions(-)
diff --git a/files/floorplans/floorplan/editor.js b/files/floorplans/floorplan/editor.js
@@ -121,6 +121,21 @@ SVG.extend(SVG.Element, {
throw new Error("Didn't find " + selector)
}
return r
+ },
+
+ touching: function(x, y, minsize) {
+ let b = this.bbox()
+ let d = minsize - b.width
+ if (d > 0) {
+ b.x -= d / 2
+ b.width = minsize
+ }
+ d = minsize - b.height
+ if (d > 0) {
+ b.y -= d / 2
+ b.height = minsize
+ }
+ return (x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height)
}
})
@@ -534,23 +549,30 @@ export class FloorplanEditor {
return this.thingAt(point, "#points")
}
- thingAt(point, selector) {
- return this.thingsAt(point, selector, 1)[0]
+ thingAt(point, selector, options) {
+ options = options ?? {}
+ options.max = 1
+ return this.thingsAt(point, selector, options)[0]
}
- thingsAt(point, selector, max) {
+ thingsAt(point, selector, options) {
+ options = options ?? {}
+
let children = this.draw.find(selector ?? "*")
.children()
.toArray()
+ let done = {}
let inside = []
- for (let i in children) {
- if (children[i].inside(point.x, point.y)) {
- if (inside.push(children[i]) >= max) {
+ for (let i = 0; i < children.length; ++i) {
+ if (children[i][options.method ?? "inside"](point.x, point.y, options.minsize)) {
+ if (inside.push(children[i]) >= options.max) {
return inside
}
+ children[i] = null
}
}
+
return inside
}
diff --git a/files/floorplans/floorplan/geometry.js b/files/floorplans/floorplan/geometry.js
@@ -16,6 +16,24 @@ SVG.extend(SVG.Circle, {
SVG.extend(SVG.Shape, {
vec: function() {
return new Vector2(this.x(), this.y())
+ },
+
+ distanceTo: function(x, y) {
+ return this.bbox().distanceTo(x, y)
+ },
+
+ touching: function(x, y, minsize) {
+ let b = this.bbox()
+ if (b.width < minsize) {
+ b.x -= (minsize - b.width) / 2
+ b.width = minsize
+ }
+ if (b.height < minsize) {
+ b.y -= (minsize - b.height) / 2
+ b.height = minsize
+ }
+ return x >= b.x && x <= b.x + b.width &&
+ y >= b.y && y <= b.y + b.height
}
})
@@ -81,9 +99,11 @@ SVG.extend(SVG.Line, {
}
},
- whereIsPoint: function(x, y) {
+ whereIsPoint: function(x, y, width) {
let p = new Vector2(x, y)
- let width = this.attr("stroke-width") ?? 1
+ if (width == null) {
+ width = this.attr("stroke-width") ?? 1
+ }
let closest = this.closestPoint(p)
/*
@@ -102,6 +122,51 @@ SVG.extend(SVG.Line, {
// This must use x and y to be compatible with Shape's inside()
inside: function(x, y) {
- return this.whereIsPoint(x, y) != null ? true : false
+ return this.whereIsPoint(x, y) != null
+ },
+
+ touching: function(x, y, width) {
+ return this.whereIsPoint(x, y, width) != null
+ },
+
+ closestEdge: function(x, y) {
+ let p = new Vector2(x, y)
+ let w = this.attr("stroke-width") ?? 1
+ let c = this.closestPoint(p)
+
+ let b = new SVG.Box(c.x - w / 2, c.y - w / 2, w, w)
+ return b.closestEdge(p.x, p.y)
+ },
+
+ distanceTo: function(x, y) {
+ return this.closestEdge(x, y).distanceTo(new Vector2(x, y))
+ }
+})
+
+SVG.extend(SVG.Box, {
+ closestEdge: function(x, y) {
+ let ex
+ if (x < this.x) {
+ ex = this.x
+ } else if (x > this.x + this.width) {
+ ex = this.x + this.width
+ } else {
+ ex = x
+ }
+ let ey
+ if (y < this.y) {
+ ey = this.y
+ } else if (y > this.y + this.height) {
+ ey = this.y + this.height
+ } else {
+ ey = y
+ }
+
+ let ev = new Vector2(ex, ey)
+ return ev
+ },
+
+ distanceTo: function(x, y) {
+ return this.closestEdge(x, y).distanceTo(new Vector2(x, y))
}
})
diff --git a/files/floorplans/floorplan/main.js b/files/floorplans/floorplan/main.js
@@ -345,22 +345,28 @@ function selectionHandler(event, editor) {
}
let p = editor.draw.point(event.clientX, event.clientY)
-
- let x = editor.thingAt(p, "#" + editor.layoutG())
- if (x) {
- x.select()
- return
+ let order = [ "#" + editor.layoutG(), "#points", "#pointmaps" ]
+ for (let i = 0; i < order.length; ++i) {
+ let x = editor.thingAt(p, order[i])
+ if (x) {
+ x.select()
+ return
+ }
}
- x = editor.thingAt(p, "#points")
- if (x) {
- x.select()
- return
+ let close = editor.thingsAt(p, order.join(","), { method: "touching", minsize: 3500 })
+ let dist
+ let closest
+ for (let i = 0; i < close.length; ++i) {
+ let tmp = close[i].distanceTo(p.x, p.y)
+ if (dist == null || tmp < dist) {
+ dist = tmp
+ closest = close[i]
+ }
}
- x = editor.thingAt(p, "#pointmaps")
- if (x) {
- x.select()
+ if (closest != null) {
+ closest.select()
return
}