mirror of
https://github.com/penpot/penpot.git
synced 2025-06-14 08:21:37 +02:00
Move kdtree, lru and heap into uxbox.util submodule.
This commit is contained in:
parent
f81542a792
commit
77933f3949
6 changed files with 94 additions and 58 deletions
|
@ -8,13 +8,15 @@
|
||||||
* @license MIT License <https://opensource.org/licenses/MIT>
|
* @license MIT License <https://opensource.org/licenses/MIT>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
goog.provide("kdtree.heap");
|
"use strict";
|
||||||
goog.provide("kdtree.heap.MinHeap");
|
|
||||||
|
goog.provide("uxbox.util.heap_impl");
|
||||||
|
goog.provide("uxbox.util.heap_impl.MinHeap");
|
||||||
|
|
||||||
goog.scope(function() {
|
goog.scope(function() {
|
||||||
"use strict";
|
const self = uxbox.util.heap_impl;
|
||||||
|
|
||||||
const compare = (x,y) => x-y;
|
const compare = (x, y) => x - y;
|
||||||
|
|
||||||
class MinHeap {
|
class MinHeap {
|
||||||
constructor(cmp) {
|
constructor(cmp) {
|
||||||
|
@ -108,5 +110,5 @@ goog.scope(function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kdtree.heap.MinHeap = MinHeap;
|
self.MinHeap = MinHeap;
|
||||||
});
|
});
|
30
src/uxbox/util/kdtree.cljs
Normal file
30
src/uxbox/util/kdtree.cljs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(ns uxbox.util.kdtree
|
||||||
|
"A cljs layer on top of js impl of kdtree located in `kdtree_impl.js`."
|
||||||
|
(:require [uxbox.util.kdtree-impl :as impl]))
|
||||||
|
|
||||||
|
(defn create
|
||||||
|
"Create an empty or initialized kd-tree instance."
|
||||||
|
([] (impl/create))
|
||||||
|
([points] (impl/create (clj->js points))))
|
||||||
|
|
||||||
|
(defn setup!
|
||||||
|
"Generate new kd-tree instance with provided generation parameter
|
||||||
|
or just return a prevuously created from internal LRU cache."
|
||||||
|
[t w h ws hs]
|
||||||
|
(impl/setup t w h ws hs))
|
||||||
|
|
||||||
|
(defn nearest
|
||||||
|
"Search nearest points to the provided point
|
||||||
|
and return the `n` maximum results."
|
||||||
|
([t p]
|
||||||
|
(nearest t p 10))
|
||||||
|
([t p n]
|
||||||
|
{:pre [(vector? p)]}
|
||||||
|
(let [p (into-array p)]
|
||||||
|
(map clj->js (impl/nearest t p n)))))
|
|
@ -13,12 +13,16 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
goog.provide("kdtree.core");
|
goog.provide("uxbox.util.kdtree_impl");
|
||||||
goog.require("kdtree.heap");
|
goog.require("uxbox.util.heap_impl");
|
||||||
goog.require("lru");
|
goog.require("uxbox.util.lru_impl");
|
||||||
goog.require("goog.asserts");
|
goog.require("goog.asserts");
|
||||||
|
|
||||||
goog.scope(function() {
|
goog.scope(function() {
|
||||||
|
const self = uxbox.util.kdtree_impl;
|
||||||
|
|
||||||
|
const heap = uxbox.util.heap_impl;
|
||||||
|
const lru = uxbox.util.lru_impl;
|
||||||
const assert = goog.asserts.assert;
|
const assert = goog.asserts.assert;
|
||||||
|
|
||||||
// Hardcoded dimensions value;
|
// Hardcoded dimensions value;
|
||||||
|
@ -76,7 +80,7 @@ goog.scope(function() {
|
||||||
function searchNearest(root, point, maxNodes) {
|
function searchNearest(root, point, maxNodes) {
|
||||||
const search = (best, node) => {
|
const search = (best, node) => {
|
||||||
if (best === null) {
|
if (best === null) {
|
||||||
best = new kdtree.heap.MinHeap((x, y) => x[1] - y[1]);
|
best = new heap.MinHeap((x, y) => x[1] - y[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let distance = precision(calculateDistance(point, node.obj));
|
let distance = precision(calculateDistance(point, node.obj));
|
||||||
|
@ -147,38 +151,7 @@ goog.scope(function() {
|
||||||
// --- Public Api
|
// --- Public Api
|
||||||
const cache = new lru.create();
|
const cache = new lru.create();
|
||||||
|
|
||||||
function create(points) {
|
|
||||||
const tree = new KDTree();
|
|
||||||
if (goog.isArray(points)) {
|
|
||||||
return initialize(tree, points);
|
|
||||||
} else {
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function generate(width, height, widthStep, heightStep) {
|
function generate(width, height, widthStep, heightStep) {
|
||||||
const key = `${width}.${height}.${widthStep}.${heightStep}`;
|
|
||||||
|
|
||||||
let tree = lru.get(cache, key);
|
|
||||||
if (tree instanceof KDTree) {
|
|
||||||
return tree;
|
|
||||||
} else {
|
|
||||||
tree = new KDTree();
|
|
||||||
setup(tree, width, height, widthStep, heightStep);
|
|
||||||
lru.set(cache, key, tree);
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize(tree, points) {
|
|
||||||
assert(goog.isArray(points));
|
|
||||||
assert(tree instanceof KDTree);
|
|
||||||
|
|
||||||
tree.root = buildTree(null, points, 0);
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup(tree, width, height, widthStep, heightStep) {
|
|
||||||
const totalSize = Math.floor((width/widthStep) * (height/heightStep));
|
const totalSize = Math.floor((width/widthStep) * (height/heightStep));
|
||||||
const points = new Array(totalSize);
|
const points = new Array(totalSize);
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
@ -189,7 +162,21 @@ goog.scope(function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(tree, points);
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup(tree, width, height, widthStep, heightStep) {
|
||||||
|
const key = `${width}.${height}.${widthStep}.${heightStep}`;
|
||||||
|
const root = lru.get(cache, key);
|
||||||
|
|
||||||
|
if (root instanceof Node) {
|
||||||
|
tree.root = root;
|
||||||
|
} else {
|
||||||
|
const points = generate(width, height, widthStep, heightStep);
|
||||||
|
tree.root = buildTree(null, points, 0);
|
||||||
|
lru.set(cache, key, tree.root);
|
||||||
|
}
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +185,23 @@ goog.scope(function() {
|
||||||
return tree.root !== null;
|
return tree.root !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initialize(tree, points) {
|
||||||
|
assert(goog.isArray(points));
|
||||||
|
assert(tree instanceof KDTree);
|
||||||
|
|
||||||
|
tree.root = buildTree(null, points, 0);
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(points) {
|
||||||
|
const tree = new KDTree();
|
||||||
|
if (goog.isArray(points)) {
|
||||||
|
return initialize(tree, points);
|
||||||
|
} else {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function clear(tree) {
|
function clear(tree) {
|
||||||
assert(tree instanceof KDTree);
|
assert(tree instanceof KDTree);
|
||||||
tree.root = null;
|
tree.root = null;
|
||||||
|
@ -212,11 +216,10 @@ goog.scope(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory functions
|
// Factory functions
|
||||||
kdtree.core.create = create;
|
self.create = create;
|
||||||
kdtree.core.generate = generate;
|
self.initialize = initialize;
|
||||||
kdtree.core.initialize = initialize;
|
self.setup = setup;
|
||||||
kdtree.core.setup = setup;
|
self.isInitialized = isInitialized;
|
||||||
kdtree.core.isInitialized = isInitialized;
|
self.clear = clear;
|
||||||
kdtree.core.clear = clear;
|
self.nearest = nearest;
|
||||||
kdtree.core.nearest = nearest;
|
|
||||||
});
|
});
|
|
@ -10,10 +10,11 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
goog.provide("lru");
|
goog.provide("uxbox.util.lru_impl");
|
||||||
goog.require("goog.asserts");
|
goog.require("goog.asserts");
|
||||||
|
|
||||||
goog.scope(function() {
|
goog.scope(function() {
|
||||||
|
const self = uxbox.util.lru_impl;
|
||||||
const assert = goog.asserts.assert;
|
const assert = goog.asserts.assert;
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
|
@ -118,8 +119,8 @@ goog.scope(function() {
|
||||||
return new Cache(limit);
|
return new Cache(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
lru.create = create;
|
self.create = create;
|
||||||
lru.get = (c, key) => c.getItem(key);
|
self.get = (c, key) => c.getItem(key);
|
||||||
lru.set = (c, key, val) => c.setItem(key, val);
|
self.set = (c, key, val) => c.setItem(key, val);
|
||||||
lru.remove = (c, key) => c.removeItem(key);
|
self.remove = (c, key) => c.removeItem(key);
|
||||||
});
|
});
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns uxbox.view
|
(ns uxbox.view
|
||||||
(:require [uxbox.view.state :as st]
|
(:require [uxbox.view.state :as st]
|
||||||
|
[uxbox.common.constants]
|
||||||
#_[uxbox.view.locales :as lc]
|
#_[uxbox.view.locales :as lc]
|
||||||
[uxbox.view.ui :as ui]))
|
[uxbox.view.ui :as ui]))
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
(ns uxbox.worker.align
|
(ns uxbox.worker.align
|
||||||
"Workspace aligment indexes worker."
|
"Workspace aligment indexes worker."
|
||||||
(:require [beicon.core :as rx]
|
(:require [beicon.core :as rx]
|
||||||
[kdtree.core :as kd]
|
[uxbox.util.kdtree :as kd]
|
||||||
[uxbox.worker.impl :as impl]
|
[uxbox.worker.impl :as impl]
|
||||||
[uxbox.common.geom.point :as gpt]))
|
[uxbox.common.geom.point :as gpt]))
|
||||||
|
|
||||||
|
@ -16,14 +16,13 @@
|
||||||
(defmethod impl/handler :grid/init
|
(defmethod impl/handler :grid/init
|
||||||
[{:keys [sender width height x-axis y-axis] :as opts}]
|
[{:keys [sender width height x-axis y-axis] :as opts}]
|
||||||
(time
|
(time
|
||||||
(let [value (kd/generate width height (or x-axis 10) (or y-axis 10))]
|
(kd/setup! tree width height (or x-axis 10) (or y-axis 10)))
|
||||||
(set! tree value)))
|
|
||||||
(impl/reply! sender nil))
|
(impl/reply! sender nil))
|
||||||
|
|
||||||
(defmethod impl/handler :grid/align
|
(defmethod impl/handler :grid/align
|
||||||
[{:keys [sender point] :as message}]
|
[{:keys [sender point] :as message}]
|
||||||
(let [point #js [(:x point) (:y point)]
|
(let [point [(:x point) (:y point)]
|
||||||
results (js->clj (kd/nearest tree point 1))
|
results (kd/nearest tree point 1)
|
||||||
[[x y] d] (first results)
|
[[x y] d] (first results)
|
||||||
result (gpt/point x y)]
|
result (gpt/point x y)]
|
||||||
(impl/reply! sender {:point (gpt/point x y)})))
|
(impl/reply! sender {:point (gpt/point x y)})))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue