mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +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
|
||||
.on('sortedImages', function(){
|
||||
BOOKMARKS.sort(imageOrderCmp)
|
||||
BOOKMARKS = fastSortGIDsByOrder(BOOKMARKS)
|
||||
})
|
||||
}
|
||||
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...
|
||||
//
|
||||
// NOTE: changing a base URL will trigger a baseURLChanged event...
|
||||
@ -638,12 +686,16 @@ function getGIDRibbonIndex(gid, data){
|
||||
// - number - ribbon index
|
||||
// - gid
|
||||
// - image
|
||||
function getRibbonGIDs(a, data){
|
||||
function getRibbonGIDs(a, no_clone, data){
|
||||
data = data == null ? DATA : data
|
||||
if(typeof(a) == typeof(123)){
|
||||
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...
|
||||
//
|
||||
// XXX try a new technique:
|
||||
// - before calling getImageBefore(..) remove the curent gid form
|
||||
// target list...
|
||||
function makePrevFromListAction(get_closest, get_list, restrict_to_ribbon){
|
||||
get_closest = get_closest == null ? getGIDBefore : get_closest
|
||||
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.
|
||||
function updateRibbonOrder(no_reload_viewer){
|
||||
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){
|
||||
reloadViewer(true)
|
||||
|
||||
@ -588,6 +588,10 @@ var KEYBOARD_CONFIG = {
|
||||
function(){ prevBookmark() }),
|
||||
']': doc('Next bookmarked image',
|
||||
function(){ nextBookmark() }),
|
||||
'{': doc('Previous unsorted section edge',
|
||||
function(){ prevUnsortedSection() }),
|
||||
'}': doc('Next unsorted section edge',
|
||||
function(){ nextUnsortedSection() }),
|
||||
|
||||
S: {
|
||||
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
|
||||
// situations the cleanup is optional...
|
||||
function cropMarkedImages(keep_ribbons, keep_unloaded_gids){
|
||||
var marked = MARKED.slice()//.sort(imageOrderCmp)
|
||||
var marked = MARKED.slice()
|
||||
|
||||
cropDataTo(marked, keep_ribbons, keep_unloaded_gids)
|
||||
|
||||
@ -249,7 +249,8 @@ function toggleAllMarks(action, mode){
|
||||
if(action == 'on'){
|
||||
var _update = function(e){
|
||||
if(MARKED.indexOf(e) < 0){
|
||||
insertGIDToPosition(e, MARKED)
|
||||
//insertGIDToPosition(e, MARKED)
|
||||
MARKED.push(e)
|
||||
updated.push(e)
|
||||
}
|
||||
}
|
||||
@ -274,6 +275,8 @@ function toggleAllMarks(action, mode){
|
||||
|
||||
res.forEach(_update)
|
||||
|
||||
MARKED = fastSortGIDsByOrder(MARKED)
|
||||
|
||||
updateImages(updated)
|
||||
|
||||
$('.viewer').trigger('togglingMarks', [updated, action])
|
||||
@ -310,12 +313,14 @@ function invertImageMarks(){
|
||||
var i = MARKED.indexOf(e)
|
||||
if(i == -1){
|
||||
on.push(e)
|
||||
insertGIDToPosition(e, MARKED)
|
||||
MARKED.push(e)
|
||||
//insertGIDToPosition(e, MARKED)
|
||||
} else {
|
||||
off.push(e)
|
||||
MARKED.splice(i, 1)
|
||||
}
|
||||
})
|
||||
MARKED = fastSortGIDsByOrder(MARKED)
|
||||
updateImages(ribbon)
|
||||
|
||||
$('.viewer')
|
||||
@ -348,7 +353,8 @@ function toggleMarkBlock(image){
|
||||
}
|
||||
// do the toggle...
|
||||
if(state){
|
||||
insertGIDToPosition(e, MARKED)
|
||||
//insertGIDToPosition(e, MARKED)
|
||||
MARKED.push(e)
|
||||
} else {
|
||||
MARKED.splice(MARKED.indexOf(e), 1)
|
||||
}
|
||||
@ -364,6 +370,8 @@ function toggleMarkBlock(image){
|
||||
var right = ribbon.slice(i+1)
|
||||
$.each(right, _convert)
|
||||
|
||||
MARKED = fastSortGIDsByOrder(MARKED)
|
||||
|
||||
updateImages(updated)
|
||||
|
||||
$('.viewer')
|
||||
@ -396,7 +404,7 @@ function shiftMarkedImages(direction, mode, new_ribbon){
|
||||
// shift all marked images...
|
||||
} else {
|
||||
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(marked, function(e){
|
||||
var i = ribbon.indexOf(e)
|
||||
@ -420,7 +428,7 @@ function shiftMarkedImages(direction, mode, new_ribbon){
|
||||
// add marked to existing ribbon...
|
||||
} else {
|
||||
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...
|
||||
@ -557,7 +565,7 @@ var loadFileMarks = makeFileLoader(
|
||||
if(DATA.version == '2.0'){
|
||||
setTimeout(function(){
|
||||
var t0 = Date.now()
|
||||
MARKED.sort(imageOrderCmp)
|
||||
MARKED = fastSortGIDsByOrder(MARKED)
|
||||
var t1 = Date.now()
|
||||
|
||||
// XXX is this the correct way to do this???
|
||||
|
||||
@ -177,7 +177,10 @@ function getClosestGIDs(gid){
|
||||
|
||||
function reverseImageOrder(){
|
||||
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_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
|
||||
images = images == null ? IMAGES : images
|
||||
|
||||
var order = DATA.order
|
||||
|
||||
for(var gid in images){
|
||||
var tags = images[gid].tags
|
||||
// no tags in this image...
|
||||
@ -40,10 +46,19 @@ function buildTagsFromImages(tagset, images){
|
||||
}
|
||||
// only update if not tagged...
|
||||
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
|
||||
}
|
||||
|
||||
@ -93,8 +108,6 @@ function addTag(tags, gid, tagset, images){
|
||||
tagset[tag] = set
|
||||
}
|
||||
if(set.indexOf(gid) < 0){
|
||||
//set.push(gid)
|
||||
//set.sort()
|
||||
insertGIDToPosition(gid, set)
|
||||
}
|
||||
|
||||
@ -193,7 +206,7 @@ function tagSelectAND(tags, from, no_sort, tagset){
|
||||
return res == null
|
||||
? []
|
||||
: res.filter(function(gid){
|
||||
// skip unloaded from...
|
||||
// skip unloaded...
|
||||
return from.indexOf(gid) >= 0
|
||||
})
|
||||
}
|
||||
@ -231,10 +244,15 @@ function tagSelectAND(tags, from, no_sort, tagset){
|
||||
}
|
||||
// populate res...
|
||||
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
|
||||
}
|
||||
|
||||
@ -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){
|
||||
list.forEach(function(gid){
|
||||
addTag(tags, gid)
|
||||
@ -353,18 +366,22 @@ function unmarkTagged(tags){
|
||||
//
|
||||
// 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){
|
||||
gids = gids == null ? getLoadedGIDs() : gids
|
||||
var list = tagSelectAND(tags, gids)
|
||||
var res = []
|
||||
|
||||
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
|
||||
// adjacent to it are not in the list...
|
||||
if(list.indexOf(gids[i-1]) < 0
|
||||
|| list.indexOf(gids[i+1]) < 0){
|
||||
if(list.indexOf(ribbon[i-1]) < 0
|
||||
|| list.indexOf(ribbon[i+1]) < 0){
|
||||
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...
|
||||
*/
|
||||
@ -411,7 +490,7 @@ function setupTags(viewer){
|
||||
})
|
||||
|
||||
}
|
||||
SETUP_BINDINGS.push(setupTags)
|
||||
//SETUP_BINDINGS.push(setupTags)
|
||||
|
||||
|
||||
// Setup the unsorted image state managers...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user