(
~visualize = {arg tiling, xOffset = 0, yOffset = 0, width = -1, height = -1, scale = 1, original = true, name = "";
	var w, h, win, view, image, subTiling, colors, colorMap, zoom = 1, svgFile;

	//tweak h and w based on difference between tiling size and requested window size
	h = if((height == -1) || (height > tiling.size), {tiling.size}, {height});
	w = if((width == -1) || (width > tiling[0].size), {tiling.collect({arg row; row.size}).maxItem}, {width});

	//instantiate window
	win = Window.new(name,
		Rect(100, 100,
			if((w * 10 * scale) < 1600, {w * 10 * scale}, {1600}),
			if((h * 10 * scale) < 900, {h * 10 * scale}, {900})), scroll: true).front;
	view = UserView(win, Rect(0, 0, w * 10 * scale, h * 10 * scale));
	image = Image.new(w * 10 * scale, h * 10 * scale);
	win.background_(Color.black);

	//get a portion of the tiling passed as an argument
	subTiling = tiling.slice((yOffset..(yOffset + h - 1)), (xOffset..(xOffset + w - 1)));

	//this maps colors to gray scale value
	//TODO: this is unecessary it is better to pass it the size of colors.
	colors = subTiling.flat.asSet.asList.sort;
	colorMap = Dictionary.new;
	colors.do({arg c, i; colorMap.put(c, Color.gray((i - (colors.size - 1)).abs / (colors.size - 1)))});

	if(original, {
		view.drawFunc = {
			//Pen.translate(0, h * 10 * scale);
			Pen.scale(scale * zoom, /*-1 **/ scale * zoom);
			Pen.width = 0.05 * scale;
			//subTiling.slice((0..(win.bounds.width / (10 * zoom) + 1)), (0..(win.bounds.height / (10 * zoom) + 1))).do({arg r;
			subTiling.slice.do({arg r;
				Pen.use {
					r.do({arg t;
						t.do({arg c;
							Pen.fillColor = colorMap[c];
							//Pen.fillColor = if(c == 4, {Color.black}, {Color.white});
							Pen.strokeColor = if(t.asSet.asList.size > 1, {Color.white}, {Color.black});
							//Pen.strokeColor = Color.white;
							Pen.moveTo(5@5);
							Pen.lineTo(0@0);
							Pen.lineTo(0@10);
							Pen.lineTo(5@5);
							Pen.fillStroke;
							Pen.rotate(0.5 * pi, 5, 5);
						});
						Pen.translate(10);
					});
				};
				Pen.translate(0, 10)
			});
		};
	}, {

		win.background_(Color.white);
		view.drawFunc = {

			//Pen.translate(0, h * 10 * scale);
			Pen.scale(scale * zoom, /*-1 **/ scale * zoom);
			Pen.width = 0.05 * scale;
			subTiling.slice.do({arg r;
				Pen.use {
					r.do({arg t;
						t.do({arg c, i;
							Pen.fillColor = Color.black;
							//Pen.fillColor = if(c == 4, {Color.black}, {Color.white});
							Pen.strokeColor = if(t.asSet.asList.size > 1, {Color.black}, {Color.black});
							//Pen.strokeColor = Color.white;
							Pen.moveTo(5@5);
							Pen.addArc(0@5, 2 * (c / colors.size) + 1, 0, 2pi);
							Pen.fillStroke;
							Pen.rotate(0.5 * pi, 5, 5);

						});
						Pen.translate(10);
					});
				};
				Pen.translate(0, 10)
			});
		};
	});


	view.keyDownAction = { |doc, char, mod, unicode, keycode, key|
		[doc, char, mod, unicode, keycode, key].postln;
	case
		// <ctrl + f> = enter full screen
		{mod == 262144 && key == 70} {win.fullScreen}
		// <esc> = exit full screen
		{mod == 0 && key == 16777216} {win.endFullScreen}
		{mod == 262144 && key == 61} {zoom = zoom * 5/4; view.refresh}
		{mod == 262144 && key == 45} {zoom = zoom * 4/5; view.refresh}
		//{mod == 262144 && key == 68} {i = Image.fromWindow(view); i.write("~/Documents/test_tile.png".standardizePath)}
		// <ctrl + d> = save
		{mod == 262144 && key == 68} {
			Dialog.savePanel({arg path;
				var img;
				img = Image.fromWindow(view);

				img.write(path /*, quality: 100 */);
				"saved".postln;
			},{
				"cancelled".postln;
			});
		}
	};

	view.refresh
};

~exportSVG = {arg tiling, xOffset = 0, yOffset = 0, width = -1, height = -1, scale = 1, name = "";
	var w, h, win, view, image, subTiling, colors, colorMap, zoom = 1, svgFile;

	//tweak h and w based on difference between tiling size and requested window size
	h = if((height == -1) || (height > tiling.size), {tiling.size}, {height});
	w = if((width == -1) || (width > tiling[0].size), {tiling.collect({arg row; row.size}).maxItem}, {width});

	//get a portion of the tiling passed as an argument
	subTiling = tiling.slice((yOffset..(yOffset + h - 1)), (xOffset..(xOffset + w - 1)));

	//this maps colors to gray scale value
	//TODO: this is unecessary it is better to pass it the size of colors.
	colors = subTiling.flat.asSet.asList.sort;
	colorMap = Dictionary.new;
	colors.do({arg c, i; colorMap.put(c, Color.gray((i - (colors.size - 1)).abs / (colors.size - 1)))});

	svgFile = SVGFile.new("~/Documents/test.svg", width: w * 10 * scale, height: h * 10 * scale);
	subTiling.slice.do({arg row, r;
		row.do({arg tile, t;
			svgFile.add(SVGCircle.new(t * 10 + 5, r * 10, 2 * (tile[1] / colors.size) + 1));
			svgFile.add(SVGCircle.new(t * 10, r * 10 + 5, 2 * (tile[0] / colors.size) + 1));
			});
		});
	svgFile.write(true);
}
)