From 23d4331881481f3988db18c47ae5f0aaca0a23e5 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 11 Oct 2026 18:18:01 +0300 Subject: [PATCH] refactoring merging and loading... Signed-off-by: Alex A. Naanou --- Viewer/features/base.js | 26 ++++++++- Viewer/features/filesystem.js | 17 +++--- Viewer/imagegrid/data.js | 103 ++++++++++++++++++++++++++++------ Viewer/imagegrid/images.js | 2 +- Viewer/imagegrid/ribbons.js | 2 +- Viewer/package-lock.json | 4 +- Viewer/package.json | 2 +- 7 files changed, 124 insertions(+), 32 deletions(-) diff --git a/Viewer/features/base.js b/Viewer/features/base.js index 43d3cbde..b7a12e5b 100755 --- a/Viewer/features/base.js +++ b/Viewer/features/base.js @@ -35,6 +35,25 @@ var core = require('features/core') /*********************************************************************/ +var merge = +module.merge = +function(base, ...others){ + // XXX get deconfliction mode... + // XXX + for(var other of others){ + base.data.join(other.data) + // deconflict... + for(var c in base.data.deconfliction ?? {}){ + var n = base.data.deconfliction[c] + other.images.replaceGID(c, n) } + delete base.data.deconfliction + base.images.join(other.images) } + return base } + + + +//--------------------------------------------------------------------- + // XXX split this into read and write actions... var BaseActions = module.BaseActions = @@ -71,7 +90,7 @@ actions.Actions({ }, - + get version(){ return version.version || '4.0.0a' }, @@ -191,6 +210,10 @@ actions.Actions({ set direction_change_steps(value){ this.config['steps-to-change-direction'] = value }, + // Helpers... + // + merge: merge, + // Consistency checking... // @@ -284,6 +307,7 @@ actions.Actions({ this.data = d.data instanceof data.Data ? d.data : data.Data(d.data) } } }], + // XXX should this clear or load empty??? // XXX should this accept args and clear specific stuff (a-la data.clear(..))??? clear: ['File/Clear', diff --git a/Viewer/features/filesystem.js b/Viewer/features/filesystem.js index 4062c70d..937a433f 100755 --- a/Viewer/features/filesystem.js +++ b/Viewer/features/filesystem.js @@ -27,6 +27,7 @@ var features = require('lib/features') var keyboard = require('lib/keyboard') var core = require('features/core') +var base = require('features/base') var widgets = require('features/ui-widgets') var overlay = require('lib/widget/overlay') @@ -366,9 +367,7 @@ var FileSystemLoaderActions = actions.Actions({ //console.log('MERGING:', k, part) logger && logger.emit('merge index', k, res) - // merge... - index.data.join(part.data) - index.images.join(part.images) } + index = that.merge(index, part) } loaded.push(k) } @@ -780,15 +779,15 @@ var FileSystemLoaderActions = actions.Actions({ var gids = imgs.keys() var new_data = that.data.constructor.fromArray(gids) - // merge with index... - // NOTE: we are prepending new images to the start... - // NOTE: all ribbon gids will change here... + // prepend to index... + var index = that.merge( + { data: new_data, images: imgs }, + that) var cur = that.data.current - that.data = new_data.join(that.data) + that.images = index.images + that.data = index.data that.data.current = cur - that.images.join(imgs) - that.reload() // XXX report that we are done... diff --git a/Viewer/imagegrid/data.js b/Viewer/imagegrid/data.js index 4cb5f0e3..ff7c8817 100755 --- a/Viewer/imagegrid/data.js +++ b/Viewer/imagegrid/data.js @@ -454,18 +454,58 @@ var DataPrototype = { && (res[i] = e) } return res }, + // Get GID conflits... + // + conflicts: function(lst){ + lst ??= this.order + var res = {} + // NOTE: this keeps the last occurrence... + var lst_idx = lst.toKeys() + if(Object.keys(lst_idx).length != lst.length){ + // NOTE: going from the tail to keep the indexes in ascending order... + for(var i=lst.length-1; i >= 0; i--){ + if(lst_idx[lst[i]] != i){ + res[lst[i]] ??= [lst_idx[lst[i]]] + res[lst[i]].unshift(i) } } } + return res }, + + // Return list of items common between .order and there... + // + // NOTE: this is optimistic and expects that there are not internal + // conflicts in either list. + // NOTE: this is mainly a proxy to Set's .intersection(..) method... + conflictsWith: function(there){ + there = new Set( + // XXX revise test... + there instanceof Array ? + there + : there.order ? + there.order + : there) + var here = new Set(this.order) + return [...here.intersection(there)] }, + // Remove duplicate items from list in-place... // + // NOTE: this is a generic array function, to remove duplicates from + // data use: .clear('duplicate') // NOTE: only the first occurrence is kept... - // NOTE: this is slow-ish... + // NOTE: this can be slow-ish... removeDuplicates: function(lst, skip_undefined=true){ + //lst ??= this.order if(skip_undefined){ - var lst_idx = lst.toKeys() - for(var i=lst.length-1; i >= 0 ; i--){ - if(lst[i] == null){ + var conflicts = this.conflicts(lst) + // merge conflicting indexes skipping the first from each set... + var remove = [] + for(var k in conflicts){ + if(k === 'undefined'){ continue } - if(lst_idx[lst[i]] != i){ - lst.splice(i, 1) } } + remove.splice(remove.length, 0, ...(conflicts[k].slice(1))) } + remove + .sort() + .reverse() + for(var i of remove){ + lst.splice(i, 1) } } else { lst.splice(0, lst.length, ...(new Set(lst))) } return lst }, @@ -558,12 +598,14 @@ var DataPrototype = { return gid } // check that the gid is unique... - while(this.ribbon_order.includes(gid) - || this.order.includes(gid)){ + while(this.has(gid)){ gid = hash(p+'-'+t+'-'+Date.now()) } return gid }, - + + has: function(gid){ + return this.ribbon_order.includes(gid) + || this.order.includes(gid) }, // Clear elements from data... // @@ -629,6 +671,8 @@ var DataPrototype = { // clear duplicates... } else if(gids == 'dup' + || gids == 'dups' + || gids == 'duplicate' || gids == 'duplicates'){ // horizontal... this.removeDuplicates(this.order) @@ -736,6 +780,7 @@ var DataPrototype = { // // NOTE: if to exists then it will get overwritten. replaceGID: function(from, to){ + // ribbon... if(from in this.ribbons){ // ribbons... var ribbon = this.ribbons[from] @@ -751,18 +796,16 @@ var DataPrototype = { to : this.__base + // image... } else { from = this.getImage(from) var i = this.getImageOrder(from) var t = this.getImage(to) - + + // do not override... (XXX) if(t != -1 && t != null){ return } - - // current... - if(from == this.current){ - this.current = to } // order... this.order[i] = to // image lists... @@ -772,7 +815,10 @@ var DataPrototype = { && (list[i] = to) }) // XXX EXPERIMENTAL: order_index delete this.order_index[from] - this.order_index[to] = i } + this.order_index[to] = i + // current... + if(from == this.current){ + this.current = to } } return this }, @@ -2524,6 +2570,12 @@ var DataPrototype = { // NOTE: this will merge the items in-place, into the method's object; // if it is needed to keep the original intact, just .clone() it... // + // If conflicts with merged data are present this will create: + // .deconfliction = { + // : , + // ... + // } + // // XXX add a 'gid' align mode... (???) // ...or should this be a .merge(..) action??? // XXX do we need to take care of gid conflicts between merged data sets??? @@ -2539,10 +2591,28 @@ var DataPrototype = { args[0] : args + // XXX add deconflicting options... + //var deconfliction = 'clear' + var deconfliction = 'merge' + var base = this args .forEach(function(data){ + // deconflicting: merge... + if(deconfliction == 'merge'){ + var conflicts = base.conflictsWith(data) + data = conflicts.length > 0 ? + data.clone() + : data + for(var c of conflicts){ + do { + var n = base.newGID() + } while(data.has(n)) + data.replaceGID(c, n) + base.deconfliction ??= {} + base.deconfliction[c] = n } } + // calculate align offset... // NOTE: negative d means we push data up while positive // pushes data down by number of ribbons... @@ -2625,8 +2695,7 @@ var DataPrototype = { s[key] = base.makeSparseImages(s[key].concat(list)) } }) }) base - // XXX this is slow-ish... - //.removeDuplicateGIDs() + // deconflicting: clear... .clear('duplicates') .clear('empty') diff --git a/Viewer/imagegrid/images.js b/Viewer/imagegrid/images.js index 73ad20af..f247508e 100755 --- a/Viewer/imagegrid/images.js +++ b/Viewer/imagegrid/images.js @@ -567,7 +567,7 @@ module.ImagesPrototype = { // Replace image gid... // - replaceGid: function(from, to){ + replaceGID: function(from, to){ var img = this[from] // XXX is the test needed here??? diff --git a/Viewer/imagegrid/ribbons.js b/Viewer/imagegrid/ribbons.js index 7406d4f7..c41fd4c3 100755 --- a/Viewer/imagegrid/ribbons.js +++ b/Viewer/imagegrid/ribbons.js @@ -1379,7 +1379,7 @@ var RibbonsPrototype = { // Replace image gid... // // XXX should this work for ribbon gids??? - replaceGid: function(from, to){ + replaceGID: function(from, to){ var img = this.getImage(from) img && img.length > 0 diff --git a/Viewer/package-lock.json b/Viewer/package-lock.json index 790773e5..7b8fa57b 100644 --- a/Viewer/package-lock.json +++ b/Viewer/package-lock.json @@ -1,12 +1,12 @@ { "name": "ImageGrid.Viewer.g4", - "version": "4.0.13a", + "version": "4.0.14a", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ImageGrid.Viewer.g4", - "version": "4.0.13a", + "version": "4.0.14a", "dependencies": { "app-module-path": "*", "async-json": "0.0.2", diff --git a/Viewer/package.json b/Viewer/package.json index 37c6e68e..6171e15e 100644 --- a/Viewer/package.json +++ b/Viewer/package.json @@ -1,7 +1,7 @@ { "name": "ImageGrid.Viewer.g4", "main": "index.html", - "version": "4.0.14a", + "version": "4.0.15a", "author": "Alex A. Naanou (https://github.com/flynx)", "contributors": [], "repository": "github:flynx/ImageGrid",