lots of stuff connected with tags and performance...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2013-12-21 09:49:15 +04:00
parent 4a45ed536f
commit 5a687e0307
6 changed files with 180 additions and 29 deletions

View File

@ -147,7 +147,7 @@ function setupBookmarks(viewer){
return viewer
.on('sortedImages', function(){
BOOKMARKS.sort(imageOrderCmp)
BOOKMARKS = fastSortGIDsByOrder(BOOKMARKS)
})
}
SETUP_BINDINGS.push(setupBookmarks)

View File

@ -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)

View File

@ -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',

View File

@ -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???

View File

@ -177,7 +177,10 @@ function getClosestGIDs(gid){
function reverseImageOrder(){
DATA.order.reverse()
updateRibbonOrder()
DATA.ribbons.forEach(function(r){
r.reverse()
})
reloadViewer(true)
}

View File

@ -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...