mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +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