mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-30 02:40:08 +00:00
lots of stuff connected with tags and performance...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
4a45ed536f
commit
5a687e0307
@ -147,7 +147,7 @@ function setupBookmarks(viewer){
|
|||||||
|
|
||||||
return viewer
|
return viewer
|
||||||
.on('sortedImages', function(){
|
.on('sortedImages', function(){
|
||||||
BOOKMARKS.sort(imageOrderCmp)
|
BOOKMARKS = fastSortGIDsByOrder(BOOKMARKS)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SETUP_BINDINGS.push(setupBookmarks)
|
SETUP_BINDINGS.push(setupBookmarks)
|
||||||
|
|||||||
63
ui/data.js
63
ui/data.js
@ -536,6 +536,54 @@ Array.prototype.binSearch = function(target, cmp, get){
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// This is a cheating fast sort...
|
||||||
|
//
|
||||||
|
// By cheating we might use more memory -- this is both not in-place
|
||||||
|
// and may use quite a bit of memory...
|
||||||
|
//
|
||||||
|
// The gain is that this is SIGNIFICANTLY faster than using
|
||||||
|
// .sort(imageOrderCmp)...
|
||||||
|
//
|
||||||
|
// The complexity here is O(N) where N is DATA.order.length rather than
|
||||||
|
// gids.length vs. O(n nog n) for the .sort(..), but the processing overhead
|
||||||
|
// is significantly smaller...
|
||||||
|
//
|
||||||
|
// Here are a couple of test runs:
|
||||||
|
//
|
||||||
|
// var t0 = Date.now()
|
||||||
|
// getRibbonGIDs()
|
||||||
|
// .slice(0, 2000)
|
||||||
|
// .sort(imageOrderCmp)
|
||||||
|
// console.log('T:', Date.now()-t0)
|
||||||
|
// >>> T: 4126
|
||||||
|
//
|
||||||
|
// var t0 = Date.now()
|
||||||
|
// fastSortGIDsByOrder(
|
||||||
|
// getRibbonGIDs()
|
||||||
|
// .slice(0,2000))
|
||||||
|
// console.log('T:', Date.now()-t0)
|
||||||
|
// >>> T: 171
|
||||||
|
//
|
||||||
|
// On the down side, this has some memory overhead -- ~ N - n * ref
|
||||||
|
//
|
||||||
|
function fastSortGIDsByOrder(gids, data){
|
||||||
|
data = data == null ? DATA : data
|
||||||
|
|
||||||
|
var order = data.order
|
||||||
|
var res = []
|
||||||
|
|
||||||
|
// insert the gids to their order positions...
|
||||||
|
gids.forEach(function(gid){
|
||||||
|
res[order.indexOf(gid)] = gid
|
||||||
|
})
|
||||||
|
|
||||||
|
// clear out the nulls...
|
||||||
|
return res.filter(function(e){
|
||||||
|
return e != null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Base URL interface...
|
// Base URL interface...
|
||||||
//
|
//
|
||||||
// NOTE: changing a base URL will trigger a baseURLChanged event...
|
// NOTE: changing a base URL will trigger a baseURLChanged event...
|
||||||
@ -638,12 +686,16 @@ function getGIDRibbonIndex(gid, data){
|
|||||||
// - number - ribbon index
|
// - number - ribbon index
|
||||||
// - gid
|
// - gid
|
||||||
// - image
|
// - image
|
||||||
function getRibbonGIDs(a, data){
|
function getRibbonGIDs(a, no_clone, data){
|
||||||
data = data == null ? DATA : data
|
data = data == null ? DATA : data
|
||||||
if(typeof(a) == typeof(123)){
|
if(typeof(a) == typeof(123)){
|
||||||
return data.ribbons[a].slice()
|
return data.ribbons[a].slice()
|
||||||
}
|
}
|
||||||
return data.ribbons[getGIDRibbonIndex(a, data)].slice()
|
var res = data.ribbons[getGIDRibbonIndex(a, data)]
|
||||||
|
if(no_clone){
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return res.slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1286,6 +1338,10 @@ function makeNextFromListAction(get_closest, get_list, restrict_to_ribbon){
|
|||||||
|
|
||||||
|
|
||||||
// see makeNextFromListAction(..) above for documentation...
|
// see makeNextFromListAction(..) above for documentation...
|
||||||
|
//
|
||||||
|
// XXX try a new technique:
|
||||||
|
// - before calling getImageBefore(..) remove the curent gid form
|
||||||
|
// target list...
|
||||||
function makePrevFromListAction(get_closest, get_list, restrict_to_ribbon){
|
function makePrevFromListAction(get_closest, get_list, restrict_to_ribbon){
|
||||||
get_closest = get_closest == null ? getGIDBefore : get_closest
|
get_closest = get_closest == null ? getGIDBefore : get_closest
|
||||||
get_list = get_list == null ? getRibbonGIDs : get_list
|
get_list = get_list == null ? getRibbonGIDs : get_list
|
||||||
@ -2113,7 +2169,8 @@ function showImage(gid){
|
|||||||
// NOTE: if no_reload_viewer is true, then no re-rendering is triggered.
|
// NOTE: if no_reload_viewer is true, then no re-rendering is triggered.
|
||||||
function updateRibbonOrder(no_reload_viewer){
|
function updateRibbonOrder(no_reload_viewer){
|
||||||
for(var i=0; i < DATA.ribbons.length; i++){
|
for(var i=0; i < DATA.ribbons.length; i++){
|
||||||
DATA.ribbons[i].sort(imageOrderCmp)
|
//DATA.ribbons[i].sort(imageOrderCmp)
|
||||||
|
DATA.ribbons[i] = fastSortGIDsByOrder(DATA.ribbons[i])
|
||||||
}
|
}
|
||||||
if(!no_reload_viewer){
|
if(!no_reload_viewer){
|
||||||
reloadViewer(true)
|
reloadViewer(true)
|
||||||
|
|||||||
@ -588,6 +588,10 @@ var KEYBOARD_CONFIG = {
|
|||||||
function(){ prevBookmark() }),
|
function(){ prevBookmark() }),
|
||||||
']': doc('Next bookmarked image',
|
']': doc('Next bookmarked image',
|
||||||
function(){ nextBookmark() }),
|
function(){ nextBookmark() }),
|
||||||
|
'{': doc('Previous unsorted section edge',
|
||||||
|
function(){ prevUnsortedSection() }),
|
||||||
|
'}': doc('Next unsorted section edge',
|
||||||
|
function(){ nextUnsortedSection() }),
|
||||||
|
|
||||||
S: {
|
S: {
|
||||||
default: doc('Start slideshow',
|
default: doc('Start slideshow',
|
||||||
|
|||||||
22
ui/marks.js
22
ui/marks.js
@ -164,7 +164,7 @@ var updateSelectedImageMark = makeMarkUpdater(
|
|||||||
// not exist, as there is no way to distinguish between the two
|
// not exist, as there is no way to distinguish between the two
|
||||||
// situations the cleanup is optional...
|
// situations the cleanup is optional...
|
||||||
function cropMarkedImages(keep_ribbons, keep_unloaded_gids){
|
function cropMarkedImages(keep_ribbons, keep_unloaded_gids){
|
||||||
var marked = MARKED.slice()//.sort(imageOrderCmp)
|
var marked = MARKED.slice()
|
||||||
|
|
||||||
cropDataTo(marked, keep_ribbons, keep_unloaded_gids)
|
cropDataTo(marked, keep_ribbons, keep_unloaded_gids)
|
||||||
|
|
||||||
@ -249,7 +249,8 @@ function toggleAllMarks(action, mode){
|
|||||||
if(action == 'on'){
|
if(action == 'on'){
|
||||||
var _update = function(e){
|
var _update = function(e){
|
||||||
if(MARKED.indexOf(e) < 0){
|
if(MARKED.indexOf(e) < 0){
|
||||||
insertGIDToPosition(e, MARKED)
|
//insertGIDToPosition(e, MARKED)
|
||||||
|
MARKED.push(e)
|
||||||
updated.push(e)
|
updated.push(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,6 +275,8 @@ function toggleAllMarks(action, mode){
|
|||||||
|
|
||||||
res.forEach(_update)
|
res.forEach(_update)
|
||||||
|
|
||||||
|
MARKED = fastSortGIDsByOrder(MARKED)
|
||||||
|
|
||||||
updateImages(updated)
|
updateImages(updated)
|
||||||
|
|
||||||
$('.viewer').trigger('togglingMarks', [updated, action])
|
$('.viewer').trigger('togglingMarks', [updated, action])
|
||||||
@ -310,12 +313,14 @@ function invertImageMarks(){
|
|||||||
var i = MARKED.indexOf(e)
|
var i = MARKED.indexOf(e)
|
||||||
if(i == -1){
|
if(i == -1){
|
||||||
on.push(e)
|
on.push(e)
|
||||||
insertGIDToPosition(e, MARKED)
|
MARKED.push(e)
|
||||||
|
//insertGIDToPosition(e, MARKED)
|
||||||
} else {
|
} else {
|
||||||
off.push(e)
|
off.push(e)
|
||||||
MARKED.splice(i, 1)
|
MARKED.splice(i, 1)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
MARKED = fastSortGIDsByOrder(MARKED)
|
||||||
updateImages(ribbon)
|
updateImages(ribbon)
|
||||||
|
|
||||||
$('.viewer')
|
$('.viewer')
|
||||||
@ -348,7 +353,8 @@ function toggleMarkBlock(image){
|
|||||||
}
|
}
|
||||||
// do the toggle...
|
// do the toggle...
|
||||||
if(state){
|
if(state){
|
||||||
insertGIDToPosition(e, MARKED)
|
//insertGIDToPosition(e, MARKED)
|
||||||
|
MARKED.push(e)
|
||||||
} else {
|
} else {
|
||||||
MARKED.splice(MARKED.indexOf(e), 1)
|
MARKED.splice(MARKED.indexOf(e), 1)
|
||||||
}
|
}
|
||||||
@ -364,6 +370,8 @@ function toggleMarkBlock(image){
|
|||||||
var right = ribbon.slice(i+1)
|
var right = ribbon.slice(i+1)
|
||||||
$.each(right, _convert)
|
$.each(right, _convert)
|
||||||
|
|
||||||
|
MARKED = fastSortGIDsByOrder(MARKED)
|
||||||
|
|
||||||
updateImages(updated)
|
updateImages(updated)
|
||||||
|
|
||||||
$('.viewer')
|
$('.viewer')
|
||||||
@ -396,7 +404,7 @@ function shiftMarkedImages(direction, mode, new_ribbon){
|
|||||||
// shift all marked images...
|
// shift all marked images...
|
||||||
} else {
|
} else {
|
||||||
var marked = MARKED.slice()
|
var marked = MARKED.slice()
|
||||||
// remove all the marked images form all the ribbons...
|
// remove all the marked images form all other ribbons...
|
||||||
$.each(DATA.ribbons, function(ribbon){
|
$.each(DATA.ribbons, function(ribbon){
|
||||||
$.each(marked, function(e){
|
$.each(marked, function(e){
|
||||||
var i = ribbon.indexOf(e)
|
var i = ribbon.indexOf(e)
|
||||||
@ -420,7 +428,7 @@ function shiftMarkedImages(direction, mode, new_ribbon){
|
|||||||
// add marked to existing ribbon...
|
// add marked to existing ribbon...
|
||||||
} else {
|
} else {
|
||||||
cur += direction == 'next' ? 1 : -1
|
cur += direction == 'next' ? 1 : -1
|
||||||
DATA.ribbons[cur] = DATA.ribbons[cur].concat(marked).sort(cmp)
|
DATA.ribbons[cur] = fastSortGIDsByOrder(DATA.ribbons[cur].concat(marked))
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove empty ribbons...
|
// remove empty ribbons...
|
||||||
@ -557,7 +565,7 @@ var loadFileMarks = makeFileLoader(
|
|||||||
if(DATA.version == '2.0'){
|
if(DATA.version == '2.0'){
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
var t0 = Date.now()
|
var t0 = Date.now()
|
||||||
MARKED.sort(imageOrderCmp)
|
MARKED = fastSortGIDsByOrder(MARKED)
|
||||||
var t1 = Date.now()
|
var t1 = Date.now()
|
||||||
|
|
||||||
// XXX is this the correct way to do this???
|
// XXX is this the correct way to do this???
|
||||||
|
|||||||
@ -177,7 +177,10 @@ function getClosestGIDs(gid){
|
|||||||
|
|
||||||
function reverseImageOrder(){
|
function reverseImageOrder(){
|
||||||
DATA.order.reverse()
|
DATA.order.reverse()
|
||||||
updateRibbonOrder()
|
DATA.ribbons.forEach(function(r){
|
||||||
|
r.reverse()
|
||||||
|
})
|
||||||
|
reloadViewer(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
113
ui/tags.js
113
ui/tags.js
@ -20,6 +20,10 @@ var UNSORTED_TAG = 'unsorted'
|
|||||||
var TAGS = {}
|
var TAGS = {}
|
||||||
|
|
||||||
|
|
||||||
|
var TAGS_FILE_DEFAULT = 'tags.json'
|
||||||
|
var TAGS_FILE_PATTERN = /^[0-9]*-tags.json$/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
@ -27,6 +31,8 @@ function buildTagsFromImages(tagset, images){
|
|||||||
tagset = tagset == null ? TAGS : tagset
|
tagset = tagset == null ? TAGS : tagset
|
||||||
images = images == null ? IMAGES : images
|
images = images == null ? IMAGES : images
|
||||||
|
|
||||||
|
var order = DATA.order
|
||||||
|
|
||||||
for(var gid in images){
|
for(var gid in images){
|
||||||
var tags = images[gid].tags
|
var tags = images[gid].tags
|
||||||
// no tags in this image...
|
// no tags in this image...
|
||||||
@ -40,10 +46,19 @@ function buildTagsFromImages(tagset, images){
|
|||||||
}
|
}
|
||||||
// only update if not tagged...
|
// only update if not tagged...
|
||||||
if(tagset[tag].indexOf(gid) < 0){
|
if(tagset[tag].indexOf(gid) < 0){
|
||||||
tagset[tag].push(gid)
|
// NOTE: this is cheating, but it's ~5x faster than
|
||||||
|
// insertGIDToPosition(..) but still 10^2 slower
|
||||||
|
// than .push(..) (unsorted tags)
|
||||||
|
tagset[tag][order.indexOf(gid)] = gid
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanup...
|
||||||
|
for(var tag in tagset){
|
||||||
|
tagset[tag] = tagset[tag].filter(function(e){ return e != null })
|
||||||
|
}
|
||||||
|
|
||||||
return tagset
|
return tagset
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +108,6 @@ function addTag(tags, gid, tagset, images){
|
|||||||
tagset[tag] = set
|
tagset[tag] = set
|
||||||
}
|
}
|
||||||
if(set.indexOf(gid) < 0){
|
if(set.indexOf(gid) < 0){
|
||||||
//set.push(gid)
|
|
||||||
//set.sort()
|
|
||||||
insertGIDToPosition(gid, set)
|
insertGIDToPosition(gid, set)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +206,7 @@ function tagSelectAND(tags, from, no_sort, tagset){
|
|||||||
return res == null
|
return res == null
|
||||||
? []
|
? []
|
||||||
: res.filter(function(gid){
|
: res.filter(function(gid){
|
||||||
// skip unloaded from...
|
// skip unloaded...
|
||||||
return from.indexOf(gid) >= 0
|
return from.indexOf(gid) >= 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -231,10 +244,15 @@ function tagSelectAND(tags, from, no_sort, tagset){
|
|||||||
}
|
}
|
||||||
// populate res...
|
// populate res...
|
||||||
if(gid != null && from.indexOf(gid) >= 0){
|
if(gid != null && from.indexOf(gid) >= 0){
|
||||||
no_sort ? res.push(gid) : insertGIDToPosition(gid, res)
|
//no_sort == true ? res.push(gid) : insertGIDToPosition(gid, res)
|
||||||
|
res.push(gid)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if(!no_sort){
|
||||||
|
fastSortGIDsByOrder(res)
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,16 +289,11 @@ function tagSelectOR(tags, from, no_sort, tagset){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// XXX don't remember the semantics...
|
/**********************************************************************
|
||||||
function getRelatedTags(){
|
* List oriented tag operations...
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
|
||||||
|
|
||||||
function tagList(list, tags){
|
function tagList(list, tags){
|
||||||
list.forEach(function(gid){
|
list.forEach(function(gid){
|
||||||
addTag(tags, gid)
|
addTag(tags, gid)
|
||||||
@ -353,18 +366,22 @@ function unmarkTagged(tags){
|
|||||||
//
|
//
|
||||||
// Essentially this will list tag block borders.
|
// Essentially this will list tag block borders.
|
||||||
//
|
//
|
||||||
|
// NOTE: this will consider each gids's ribbon context rather than the
|
||||||
|
// straight order context...
|
||||||
|
// XXX this is slow...
|
||||||
function listTagsAtGapsFrom(tags, gids){
|
function listTagsAtGapsFrom(tags, gids){
|
||||||
gids = gids == null ? getLoadedGIDs() : gids
|
gids = gids == null ? getLoadedGIDs() : gids
|
||||||
var list = tagSelectAND(tags, gids)
|
var list = tagSelectAND(tags, gids)
|
||||||
var res = []
|
var res = []
|
||||||
|
|
||||||
list.forEach(function(gid){
|
list.forEach(function(gid){
|
||||||
var i = gids.indexOf(gid)
|
var ribbon = DATA.ribbons[getGIDRibbonIndex(gid)]
|
||||||
|
var i = ribbon.indexOf(gid)
|
||||||
|
|
||||||
// add the current gid to the result iff one or both gids
|
// add the current gid to the result iff one or both gids
|
||||||
// adjacent to it are not in the list...
|
// adjacent to it are not in the list...
|
||||||
if(list.indexOf(gids[i-1]) < 0
|
if(list.indexOf(ribbon[i-1]) < 0
|
||||||
|| list.indexOf(gids[i+1]) < 0){
|
|| list.indexOf(ribbon[i+1]) < 0){
|
||||||
res.push(gid)
|
res.push(gid)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -373,6 +390,44 @@ function listTagsAtGapsFrom(tags, gids){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// XXX these are still slow -- there is no need to re-index the whole
|
||||||
|
// data on each jump...
|
||||||
|
function nextGapEdge(tags, gids){
|
||||||
|
var res = getGIDAfter(getImageGID(), listTagsAtGapsFrom(tags, gids))
|
||||||
|
|
||||||
|
if(res == null){
|
||||||
|
flashIndicator('end')
|
||||||
|
return getImage()
|
||||||
|
}
|
||||||
|
return showImage(res)
|
||||||
|
}
|
||||||
|
function prevGapEdge(tags, gids){
|
||||||
|
var cur = getImageGID()
|
||||||
|
var targets = listTagsAtGapsFrom(tags, gids)
|
||||||
|
|
||||||
|
// drop the current elem if it's in the list...
|
||||||
|
var i = targets.indexOf(cur)
|
||||||
|
if(i >= 0){
|
||||||
|
targets.splice(i, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = getGIDBefore(cur, targets)
|
||||||
|
|
||||||
|
if(res == null){
|
||||||
|
flashIndicator('start')
|
||||||
|
return getImage()
|
||||||
|
}
|
||||||
|
return showImage(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function nextUnsortedSection(){
|
||||||
|
return nextGapEdge('unsorted')
|
||||||
|
}
|
||||||
|
function prevUnsortedSection(){
|
||||||
|
return prevGapEdge('unsorted')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
@ -389,6 +444,30 @@ function cropTagged(tags, keep_ribbons, keep_unloaded_gids){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Files...
|
||||||
|
*/
|
||||||
|
|
||||||
|
// XXX need to detect if we loaded the tags and if so not call
|
||||||
|
// buildTagsFromImages(..)...
|
||||||
|
var loadFileTags = makeFileLoader(
|
||||||
|
'Tags',
|
||||||
|
TAGS_FILE_DEFAULT,
|
||||||
|
TAGS_FILE_PATTERN,
|
||||||
|
function(data){
|
||||||
|
TAGS = data
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Save image marks to file
|
||||||
|
var saveFileMarks = makeFileSaver(
|
||||||
|
TAGS_FILE_DEFAULT,
|
||||||
|
function(){
|
||||||
|
return TAGS
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* Setup...
|
* Setup...
|
||||||
*/
|
*/
|
||||||
@ -411,7 +490,7 @@ function setupTags(viewer){
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
SETUP_BINDINGS.push(setupTags)
|
//SETUP_BINDINGS.push(setupTags)
|
||||||
|
|
||||||
|
|
||||||
// Setup the unsorted image state managers...
|
// Setup the unsorted image state managers...
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user