Clone in a terminal:
Commit 7d8765f
Johan Mattsson - 2012-07-28T14:37:11+00:00
7d8765fcbf2133d3052d4f7692a81570f7d4199ejohan.mattsson.m@gmail.com

a sorted table for glyph collections

Changed files (7)

Diff

diff --git a/src/Font.vala b/src/Font.valaindex cf8ed9d..5901f4b 100644--- a/src/Font.vala+++ b/src/Font.vala@@ -22,11 +22,10 @@ namespace Supplement {  class Font : GLib.Object { 	-	// TODO implemet sorted table:-	HashTable <string, GlyphCollection> glyph_cache = new HashTable <string, GlyphCollection> (str_hash, str_equal);+	GlyphTable glyph_cache = new GlyphTable (); 	 	/** Glyphs that not are tied to a unichar value. */-	HashTable <string, GlyphCollection> unassigned_glyphs = new HashTable <string, GlyphCollection> (str_hash, str_equal);+	GlyphTable unassigned_glyphs = new GlyphTable (); 	 	/** Last unassigned index */ 	int next_unindexed = 0;@@ -84,7 +83,7 @@ class Font : GLib.Object { 	}  	public uint get_length () {-		return glyph_cache.get_keys ().length ();+		return glyph_cache.length () + unassigned_glyphs.length (); 	}  	/** Retuns true if the current font has be modified */@@ -260,27 +259,31 @@ class Font : GLib.Object { 		 		gr.sort (); 		-		gr.unassigned = unassigned_glyphs.get_keys ();+		gr.set_unassigned (unassigned_glyphs); 		 		return gr; 	} 	-	public void add_glyph (Glyph glyph) {-		unowned GlyphCollection? gc = glyph_cache.lookup (glyph.get_name ());+	public void add_glyph (Glyph glyph) +		requires (glyph.get_name () != "")+	{+		GlyphCollection? gc = glyph_cache.get (glyph.get_name ());  		if (gc == null) {-			glyph_cache.insert (glyph.get_name (), new GlyphCollection (glyph));+			glyph_cache.insert (new GlyphCollection (glyph)); 		} 	} -	public void add_glyph_collection (GlyphCollection glyph_collection) {-		unowned GlyphCollection? gc = glyph_cache.lookup (glyph_collection.get_name ());--		if (unlikely (gc != null)) {-			warning ("Glyph collection exists.");+	public void add_glyph_collection (GlyphCollection glyph_collection) +		requires (glyph_collection.get_name () != "")+	{+		GlyphCollection? gc = get_glyph_collection (glyph_collection.get_name ());+		+		if (gc != null) {+			warning ("glyph has been inserted"); 		} 		-		glyph_cache.insert (glyph_collection.get_name (), glyph_collection);+		glyph_cache.insert (glyph_collection); 	} 		 	public void delete_glyph (string glyph) {@@ -289,10 +292,10 @@ class Font : GLib.Object { 	 	/** Obtain all versions and alterntes for this glyph. */ 	public GlyphCollection? get_glyph_collection (string glyph) {-		GlyphCollection? gc = glyph_cache.lookup (glyph);+		GlyphCollection? gc = glyph_cache.get (glyph); 		 		if (gc == null) {-			gc = unassigned_glyphs.lookup (glyph);+			gc = unassigned_glyphs.get (glyph); 			 			if (gc == null) {			 				return null;@@ -319,25 +322,20 @@ class Font : GLib.Object { 	} 	 	public Glyph? get_glyph_indice (unichar glyph_indice) {-		List<unowned GlyphCollection> gl;-		unowned GlyphCollection gc;+		GlyphCollection gc; 		-		if (glyph_indice >= glyph_cache.size () + unassigned_glyphs.size ()) {			+		if (glyph_indice >= length ()) { 			return null;-		}+		}  		-		if (glyph_indice >= glyph_cache.size ()) {-			gl = unassigned_glyphs.get_values ();-			glyph_indice -= glyph_cache.size ();-			return null;+		if (glyph_indice >= glyph_cache.length ()) {+			glyph_indice -= glyph_cache.length ();+			gc = (!) unassigned_glyphs.nth (glyph_indice); 		} else {-			gl = glyph_cache.get_values ();+			gc = (!) glyph_cache.nth (glyph_indice); 		}-			-		gc = gl.nth (glyph_indice).data;-		return ((!) gc).get_current ();--		return null;+				+		return gc.get_current (); 	} 	 	public void add_background_image (string file) {@@ -469,7 +467,7 @@ class Font : GLib.Object { 				os.put_string ("\n"); 			} 			-			glyph_cache.for_each ((k, gc) => {+			glyph_cache.for_each ((gc) => { 				try { 					bool selected; 					foreach (Glyph g in gc.get_version_list ().glyphs) {@@ -566,12 +564,11 @@ class Font : GLib.Object { 	}  	public uint length () {-		return glyph_cache.get_keys ().length () + unassigned_glyphs.get_keys ().length ();+		return glyph_cache.length () + unassigned_glyphs.length (); 	}  	public bool is_empty () {-		uint len = glyph_cache.get_keys ().length ();-		return (len == 0);+		return (glyph_cache.length () == 0 && unassigned_glyphs.length () == 0); 	}  	public bool load (string path) {@@ -596,23 +593,33 @@ class Font : GLib.Object {  	/** Callback function for loading glyph in a separate thread. */ 	public void add_glyph_callback (Glyph g) {-		GlyphCollection? gc;	-				-		gc = get_glyph_collection (g.get_name ());+		GlyphCollection? gcl;+		GlyphCollection gc;+		+		gcl = get_glyph_collection (g.get_name ());+		+		if (gcl != null) {+			warning ("glyph does already exist");+		} 		 		if (g.is_unassigned ()) {-			gc = new GlyphCollection ();-			unassigned_glyphs.insert (@"($(++next_unindexed))", (!) gc);-			((!)gc).insert_glyph (g, true);-		} else if (gc == null) {-			gc = new GlyphCollection ();-			glyph_cache.insert (g.get_name (), (!) gc);-			((!)gc).insert_glyph (g, true);	+			gc = new GlyphCollection (g);+			g.name = @"($(++next_unindexed))";+			unassigned_glyphs.insert ((!) gc);+		} else if (gcl == null) {+			+			print (@"g.name $(g.name)\n");+			+			gc = new GlyphCollection (g);+			+			print (@"gc.get_name () $(((!)gc).get_name ())\n");+			+			glyph_cache.insert ((!) gc); 		} else { 			stderr.printf (@"Glyph collection does already have an entry for $(g.get_name ()) char $((uint64) g.unichar_code).\n");-			gc = new GlyphCollection ();-			unassigned_glyphs.insert (@"($(++next_unindexed))", (!) gc);-			((!)gc).insert_glyph (g, true);	+			gc = new GlyphCollection (g);+			g.name = @"($(++next_unindexed))";+			unassigned_glyphs.insert ((!) gc); 		} 		 		// take xheight from appropriate lower case letter@@ -888,12 +895,17 @@ class Font : GLib.Object { 		 		gc = get_glyph_collection (g.get_name ()); 		+		if (g.get_name () == "") {+			warning ("No name set for glyph.");+		}+				 		if (gc == null) {-			gc = new GlyphCollection ();-			glyph_cache.insert (g.get_name (), (!) gc);+			gc = new GlyphCollection (g);+			glyph_cache.insert ((!) gc);+		} else {+			((!)gc).insert_glyph (g, selected); 		} 		-		((!)gc).insert_glyph (g, selected); 	} 	 	/** Parse visual objects and paths */diff --git a/src/GlyphCollection.vala b/src/GlyphCollection.valaindex 23b90c5..e5a50ad 100644--- a/src/GlyphCollection.vala+++ b/src/GlyphCollection.vala@@ -39,6 +39,13 @@ class GlyphCollection : GLib.Object { 	 	public void insert_glyph (Glyph g, bool selected) { 		versions.add_glyph (g, selected);+		+		print (@"insert $(g.get_name ()) at $(versions.glyphs.length ())\n");+		assert (versions.glyphs.length () > 0);+	}+	+	public uint length () {+		return versions.glyphs.length (); 	} 	 	public string get_name () {diff --git a/src/GlyphRange.vala b/src/GlyphRange.valaindex a98f998..c99680c 100644--- a/src/GlyphRange.vala+++ b/src/GlyphRange.vala@@ -22,7 +22,7 @@ class GlyphRange { 	List<UniRange> ranges; 	List<unichar> index_start; -	public List<unowned string> unassigned;+	GlyphTable? unassigned = null; 	 	unichar len = 0; 	@@ -31,6 +31,10 @@ class GlyphRange { 	public GlyphRange () { 	} 	+	public void set_unassigned (GlyphTable? unassigned) {+		this.unassigned = unassigned;+	}+	 	public unowned List<UniRange> get_ranges () { 		return ranges; 	}@@ -96,7 +100,13 @@ class GlyphRange { 	} 	 	public unichar get_length () {-		return len + unassigned.length ();+		unichar l = len;+		+		if (unassigned != null) {+			l += ((!) unassigned).length ();+		}+		+		return l; 	} 	 	public void add_range (unichar start, unichar stop) {@@ -145,11 +155,11 @@ class GlyphRange { 			if (index > ranges.length ()) { 				i = (int) (index - ranges.length ()); 				-				if (i >= unassigned.length ()) {+				if (unassigned == null || i >= ((!) unassigned).length ()) { 					return "\0".dup(); 				} 				-				return unassigned.nth (i).data.dup();+				return ((!) ((!) unassigned).nth (i)).get_name (); 			} 		} diff --git a/src/GlyphTable.vala b/src/GlyphTable.valanew file mode 100644index 0000000..c924b34--- /dev/null+++ b/src/GlyphTable.vala@@ -0,0 +1,430 @@+/*+    Copyright (C) 2012 Johan Mattsson++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.+*/++namespace Supplement {++/** A sorted table of glyphs with search index. */+class GlyphTable : GLib.Object {+	List<Item> data = new List<Item> ();+	+	List<RowItem> row1 = new List<RowItem> ();+	List<RowItem> row2 = new List<RowItem> ();+	List<RowItem> row3 = new List<RowItem> ();+	+	public GlyphTable () {+	}++	public void remove_all () {+		while (data.length () > 0) {+			data.remove_link (data.first ());+		}+	}++	public void @for_each (Func<GlyphCollection> func) {+		data.foreach ((v) => {+			func (v.glyph_collection);+		});+	}++	unowned List<Item>? find (string n) {+		return find_index (n);+	}++	unowned List<Item>? find_next (string n) {+		return find_next_index (n);+	}+		+	public void remove (string name) {+		unowned List<Item>? d = find (name);+		+		if (d == null) {+			warning (@"did not find $name");+			return;+		}+		+		data.remove_link ((!) d);+		build_index ();+	}++	public GlyphCollection? nth (uint i) {+		unowned List<Item>? d = data.nth (i);+		+		if (d == null) {+			warning (@"no glyph for index $i");+			return null;+		}+		+		return ((!) d).data.glyph_collection;+	}++	public uint length () {+		return data.length ();+	}++	public GlyphCollection? @get (string name) {+		unowned List<Item>? d = find_index (name);+		+		if (d == null) {+			return null;+		}+		+		return ((!) d).data.glyph_collection;+	}++	public bool insert (GlyphCollection g) {+		Item item;+		unowned List<Item>? next;+		+		if (g.get_name () == "") {+			warning (@"No proper name for glyph collection. g.get_name (): \"$(g.get_name ())\"");+			return false;+		}++		if (find (g.get_name ()) != null) {+			warning (@"Table does already contain character \"$(g.get_name ())\".");+			return false;+		}+				+		next = find_next (g.get_name ());+		+		item = new Item (g.get_name (), g);+		if (next == null) {+			data.append (item);+		} else {+			data.insert_before ((!) next, item); // _sorted+		}+		+		build_index ();+		+		if (find (g.get_name ()) == null) {+			warning (@"Can not find glyph \"$(g.get_name ())\".");+			print_all ();+			return false;+		}+		+		return true;+	}++	public void print_all () {+		int indice = 0;+		foreach (Item i in data) {+			print (@"$(i.name) indice: $(indice)\n");+			indice++;+		}+		+		print ("Index:\n");+		print ("\n");+		print ("row3\n");+		foreach (RowItem i in row3) {+			print (@"$(i.item.data.name)\n");+		}+		+		print ("\n");+		print ("row2\n");+		foreach (RowItem i in row2) {+			print (@"$(i.item.data.name)\n");+		}+		+		print ("\n");+		print ("row1\n");+		foreach (RowItem i in row1) {+			print (@"$(i.item.data.name)\n");+		}		+	}++	static int compare (string an, Item b) {+		string? bt = b.glyph_collection.get_name ();+		string bn = (!) bt;+		+		uint32 ac = 0;+		uint32 bc = 0;+				+		int i1 = 0;+		int i2 = 0;+		+		return_if_fail (bt != null);+		+		int ca = an.char_count ();+		int cb = bn.char_count ();+		+		if (ca != cb) {+			return ca < cb ? 1 : -1;+		}+		+		while (true) {+			an.get_next_char (ref i1, out ac);+			bn.get_next_char (ref i2, out bc);+			+			if (ac == bc && ac != 0) {+				continue;+			}+			+			if (ac == bc) return 0;+			+			return (ac < bc) ? 1 : -1;+		}+		+		if (ac == bc) {+			return 0;+		}+		+		return (ac < bc) ? 1 : -1;+	}+	+	public void build_index () {+		uint len;+		RowItem r;+		+		while (row1.length () > 0) {+			row1.remove_link (row1.first ());+		}++		while (row2.length () > 0) {+			row2.remove_link (row2.first ());+		}++		while (row3.length () > 0) {+			row3.remove_link (row3.first ());+		}++		if (data.length () == 0) {+			return;+		}+				+		// row 1+		len = data.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.item = data.nth (i * 5);+			r.level = 1;+			row1.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 1;+		r.last = true;+		row1.append (r);	+		+		// row 2+		len = row1.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.next_row = row1.nth (i * 5);+			r.item = r.next_row.data.item; +			r.level = 2;+			row2.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 2;+		r.next_row = row1.last ();		+		r.last = true;+		row2.append (r);+		+		// row 3+		len = row2.length () / 5;+		if (len == 0) len++;+		for (int i = 0; i < len; i++) {+			r = new RowItem ();+			r.next_row = row2.nth (i * 5);+			r.item = r.next_row.data.item; +			r.level = 3;+			row3.append (r);+		}+		r = new RowItem ();+		r.item = data.last ();+		r.level = 3;+		r.next_row = row2.last ();+		r.last = true;	+		row3.append (r);+	}++	unowned List<Item>? find_next_index (string n) {+		unowned List<Item>? next = find_next_via_index (n);+		+		if (next != null) {+			/*+			if (compare (n, ((!)next).data) == 0) { // not unique+				warning (@"Index is not unique $n == $(((!)next).data.name)");+			}+			*/+			//return ((!)next).prev;+		}+	+		return next;+	}++	unowned List<Item>? find_index (string n) {+		unowned List<Item>? next = find_next_via_index (n);+		+		if (next == null) {+			return null;+		}+		+		if (compare (n, ((!)next).data) == 0) {+			return next;+		}+	+		if (((!)next) != data.first () && compare (n, ((!)next).prev.data) == 0) {+			return ((!)next).prev; // fetch previous since we are searching for the next one+		}+				+		return null; +	}+	+	unowned List<Item>? find_next_via_index (string n) {+		unowned List<Item> i = data.first ();+		unowned List<RowItem> r;+		unowned List<Item> start, stop;+		int cmp;+		+		if (data.length () == 0) {+			return null;+		}++		r = row3.first ();+		int ti = 0;+		while (true) {+			cmp = compare (n, r.data.item.data);+			+			ti++;+			+			if (cmp == 1) {+				break;+			}+			+			if (cmp == 0) {+				return r.data.item;+			}+			+			if (r != row3.last ()) {+				r = r.next;+			} else {+				return null;+			}+		} +		r = r.data.next_row;++		while (true) {+			cmp = compare (n, r.data.item.data);+			if (cmp == -1) {+				break;+			}++			if (cmp == 0) {+				return r.data.item;+			}+						+			if (r == row2.first ()) {;+				return r.data.item;+			} else r = r.prev;+		}+		r = r.data.next_row;+		+		while (true) {+			cmp = compare (n, r.data.item.data);+			if (cmp == 1) {+				break;+			}+			+			if (cmp == 0) {+				return r.data.item;+			}+								+			if (r == row1.last ()) return null;+			else r = r.next;+		}+		i = r.data.item;+		+		+		while (true) {+			cmp = compare (n, i.data);++			if (cmp == -1) {+				// null?+				return i.next;+			}++			if (cmp == 0) {+				return i;+			}+						+			if (i == data.first ()) {+				return i;+			} else { +				i = i.prev;+			}+		}+		+		return null;				+	}++	public bool validate_index () {+		int cmp;+		Item prev;+		int index;+		+		if (data.length () == 0) {+			return true;+		}+		+		prev = data.first ().data;+		index = 0;+		foreach (Item i in data) {+			cmp = compare (prev.name, i);+		+			if (cmp == 1) {+				continue;+			}+						+			if (cmp == 0 && index != 0) {+				warning (@"Value is not unique: $(i.name).");+				return false;+			}++			if (cmp == -1) {+				warning (@"Value is not sorted: $cmp == cmp $(prev.name) $(i.name).");+				return false;+			}+			+			index++;+		}+	+		return true;+	}++	class Item : GLib.Object {+		public string name;+		public GlyphCollection glyph_collection;+		+		public Item (string n, GlyphCollection gc) {+			glyph_collection = gc;+			name = n;+		}+	}++	class RowItem : GLib.Object {+		public unowned List<RowItem> next_row;+		public unowned List<Item> item;+		public int level = 0;+		public bool last = false;+	}+}++}diff --git a/src/TestSupplement.vala b/src/TestSupplement.valaindex a5b1e68..bc8eb24 100644--- a/src/TestSupplement.vala+++ b/src/TestSupplement.vala@@ -33,7 +33,8 @@ class TestCases { 	public List<Test> test_cases;
 
 	public TestCases () {
-		add (test_active_edit_point, "Active edit point");
+		add (test_glyph_table, "Glyph table");
+/*		add (test_active_edit_point, "Active edit point");
 		add (test_hex, "Unicode hex values");
 		add (test_reverse_path, "Reverse path");
 		add (test_reverse_random_paths, "Reverse random paths");
@@ -42,6 +43,69 @@ class TestCases { 		add (test_delete_points, "Delete edit points");
 		add (test_view_result, "View result in web browser");
 		add (test_save_backup, "Save backup");
+*/	}
+
+	public static void test_glyph_table () {
+		GlyphTable table = new GlyphTable ();
+		List<GlyphCollection> gc = new List<GlyphCollection> ();
+		List<unowned GlyphCollection> gc_copy;
+		GlyphCollection g;
+		
+		return_if_fail (table.length () == 0);
+		return_if_fail (table.get ("Some glyph") == null);
+		
+		// generate test data
+		for (uint i = 0; i < 1000; i++) {
+			gc.append (new GlyphCollection (new Glyph (@"TEST $i", i + 'a')));
+		}
+
+		// insert in random order
+		gc_copy = gc.copy ();
+		for (uint i = 0; gc_copy.length () > 0; i++) {
+			int t = (int) ((gc_copy.length () - 1) * Random.next_double ());
+			g = gc_copy.nth (t).data;
+			
+			if (!table.insert (g)) {
+				warning (@"Failed to insert $(g.get_name ())");
+				return;
+			}
+			
+			gc_copy.remove_all (g);
+			
+			if (!table.validate_index ()) {
+				table.print_all ();
+				warning ("index is invalid");
+				return;
+			}
+		}
+		
+		return_if_fail (table.length () == gc.length ());
+		
+		// validate table
+		for (uint i = 0; i > 1000; i++) {
+			g = (!) table.get (gc.nth (i).data.get_name ());
+			return_if_fail (gc.nth (i).data == g);
+		}
+		
+		// search 
+		for (int i = 0; i < 2000; i++) {
+			int t = (int) (999 * Random.next_double ());
+			if (table.get (@"TEST $t") == null) {
+				table.print_all ();
+				warning (@"Did't find TEST $t in glyph table.");
+				return;
+			}
+		}
+
+		// remove
+		table.remove ("TEST 0");
+		table.remove ("TEST 53");
+		return_if_fail (table.get ("TEST 0") == null);
+		return_if_fail (table.get ("TEST 53") == null);
+		
+		// search 
+		return_if_fail (table.get ("TEST 52") != null);
+		return_if_fail (table.get ("TEST 54") != null);
 	}
 
 	public static void test_delete_points () {
diff --git a/src/VersionList.vala b/src/VersionList.valaindex 4eed959..dae1df5 100644--- a/src/VersionList.vala+++ b/src/VersionList.vala@@ -49,7 +49,7 @@ class VersionList : DropMenu { 	 	public Glyph get_current () { 		if (unlikely (current_version >= glyphs.length ())) {-			warning (@"current_version >= glyphs.length ($current_version)");+			warning (@"current_version >= glyphs.length ($current_version >= $(glyphs.length ()))"); 			return new Glyph (""); 		} 		diff --git a/src/wscript b/src/wscriptindex 3580544..65bb8ba 100644--- a/src/wscript+++ b/src/wscript@@ -19,6 +19,7 @@ SUPPLEMENT_MAIN = [ 	'GlyphCanvas.vala',
 	'GlyphCollection.vala',
 	'GlyphRange.vala',
+	'GlyphTable.vala',
 	'GridTool.vala',
 	'Icons.vala',
 	'Kerning.vala', 

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112