<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox where print preview displays the noscript content */
noscript {display:none;}
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

Also see AdvancedOptions
This is very simple archive file based on [[LZ4|http://code.google.com/p/lz4/]] compression (and [[xxHash|http://code.google.com/p/xxhash/]] for file checking). 
Main purpose of this archive format is to store data for games (textures, meshes, scripts, etc.). 
Loading and uncompressing of ~LZ4 compressed data usually will be faster when loading uncompressed files directly from filesystem. It uses one call to ~LZ4_compressHC/~LZ4_uncompress per file, so files larger 1.9 GB are not supported.

Source code include:
* lz4archive/ - sample console app for packing/testing/unpacking archive files, uses C++ fstreams for IO
* lz4a_wcx/ - Total Commander archive plugin source, uses winapi memory mapped files for IO
* lz4/ - copy of ~LZ4 files, replace them with latest version from http://code.google.com/p/lz4/
* xxhash/ - copy of xxHash files, replace them with latest version from http://code.google.com/p/xxhash/

License: public domain

How to use this code to load data from archive in your code:
#include "lz4archive.h"

void load_data()
	// constructor will throw std::runtime_error if data.lz4a can't be opened or is corrupt
	lz4a::package mydata( "data.lz4a" );	

	// this will throw std::runtime_error if there is no such file in archive
	lz4a::file_data mytexture = mydata.open( "textures/texture.dds" );
	// use loaded texture:
	char const * dds_data = mytexture.begin(); // pointer to loaded "textures/texture.dds" bytes
	unsigned dds_size = mytexture.size(); // size of "textures/texture.dds"

	// non-throwing version:
	lz4a::file_data myshader;
	if( mydata.open( "shaders/shader.glsl", myshader ) )
		char const * shader_data = myshader.begin();
		unsigned shader_size = myshader.size();

	// lz4a::file_data is guarantied to be valid only until it's lz4a::package is alive
	// in cases of non-compressible files it will contain pointers directly into file mapping,
	// not into specially allocated buffer

* bin/lz4archive.exe - sample packer/tester/unpacker
* bin/lz4a.wcx - Total Commander plugin x86
* bin/lz4a.wcx64 - Total Commander plugin x64

[[Get source code|http://www.rusteddreams.net/downloads/lz4a.zip]]
For my own 3d object loading framework I've tried few different triangulators. And after some test decided to stick to very simple ear-cutting algorithm. Ear is a three consecutive polygon vertices forming convex triangle which didn't contain any other polygon vertices inside it. In my implementation I always choose an ear with smallest edge (it's not only looks better then choosing first found ear but also produces more fillrate-friendly triangulation - see [[this post by Humus|http://www.humus.name/index.php?page=News&ID=228]] for more info).

Basic outline of algorithm looks like this:
// calculate average polygon normal:
vec3 average_n = vec3( 0, 0, 0 );
for( i = 1; i < num_verts; ++i )
	average_n += cross( v[i] - v[0], v[i+1] - v[0] );

// then calculate ear metric for each polygon vertex:
if( dot( cross( next_vertex - this_vertex, prev_vertex - this_vertex ), average_n ) > 0 
	&& no_other_vertices_in_tri( prev_vertex, this_vertex, next_vertex ) )
	// valid ear
	metric = length_squared( prev_vertex - next_vertex );
	metric = FLT_MAX;

// after that 'cut' ear triangle with lowest metric and update metric for it's prev_vertex and next_vertex
// repeat cutting until number of vertices is more then 3

And as optimization don't forget to create special case versions for triangles and quads since most polygons in 3d meshes rarely have more then four vertices.
Usually, vector transformation by quaternion is derived from v' = q * v * q^^-1^^ equation or just by converting to 3*3 matrix and then by regular matrix*vector multiplication. Both approaches produce too many operations. 
In many cases simpler form is possible: most often quaternion represent only rotation and have unit length. In this case transform can be expressed as a small GLSL function:
vec3 quat_transform( vec4 q, vec3 v )
	return v + 2.*cross( q.xyz, cross( q.xyz, v ) + q.w*v ); 
There is a useful extension NV_depth_clamp on NVIDIA cards. And if you want to use it but don't want to limit yourself to running only on NVIDIA cards you'll need a way to emulate it on other hardware.
Fortunately you can do it with shader code, here is example of such depth-only writing shader.
First, we clamp vertex clip coordinate in vertex shader:

varying float Zwindow;

void main()
	vec4 clip = ftransform();
	// assuming default [0,1] glDepthRange parameters
	Zwindow = 0.5*clip.z/clip.w + 0.5;	
#if defined DEPTH_CLAMP_NEAR && defined DEPTH_CLAMP_FAR
	// clamp to both near and far
	clip.z = clamp( clip.z, -clip.w, clip.w );
#elif defined DEPTH_CLAMP_NEAR
	// clamp only to near plane, far is clipped
	clip.z = max( clip.z, -clip.w );
#elif defined DEPTH_CLAMP_FAR
	// clamp only to far plane, near is clipped
	clip.z = min( clip.z, clip.w );
	gl_Position = clip;

Second, we need to recalculate correct per-fragment window depth in fragment shader:

varying float Zwindow;

void main()
#if defined DEPTH_CLAMP_NEAR && defined DEPTH_CLAMP_FAR
	gl_FragDepth = clamp( Zwindow, 0., 1. );
#elif defined DEPTH_CLAMP_NEAR
	gl_FragDepth = max( Zwindow, 0. );
#elif defined DEPTH_CLAMP_FAR
	gl_FragDepth = min( Zwindow, 1. );

Update: Patrick Cozzi from http://blogs.agi.com/insight3d/ noted what this won't work correctly in case of perspective projection, here is his comment:
I just wanted to let you know that a small modification is required for perspective projections.  Since varying variables are interpolated in a perspective correct fashion, Zwindow isn’t want you’d expect when it gets to the fragment shader.  On shader model 4 cards, this should be easy to correct by prefixing “varying float Zwindow” with “noperspective.”  This didn’t work on my Radeon HD 4350 – probably a driver bug.  But it is still possible to undo the perspective-correct part of the interpolation.  First multiple Zwindow by w in the vertex shader:
	Zwindow = (0.5 * (clip.z / clip.w) + 0.5) * clip.w;
Then multiple by 1/w in the fragment shader, for example:
	gl_FragDepth = min(Zwindow * gl_FragCoord.w, 1.0);  // really 1/w
Some time ago, then shader hardware can handle very limited number of instructions, I was making water waves shader for [[flower screensaver|http://www.rusteddreams.net/flower.html]]. Surface movement actually was a sum of 4 sine waves of different directions, periods and amplitudes. Result of cosine function is also needed to calculate normal of resulting surface. 

So I've just plugged glsl sin() function and everything was fine until I tried to run it on GeForce 3 card there it didn't fit into available number of instructions (this card didn't support native sin and cos instructions, so glsl compiler emulated them with some math).

Water waves didn't need that much precision in calculating trig functions so I decided to replace them with some coarse approximation.

There are many cases then you don't need exact function. And waves calculation is one such example: actually, sum of sine waves is just a 'fake' in same way as sum of any other functions (compared to real water surface simulation).

So lets look at basic Hermite curve: -2x^^3^^ + 3x^^2^^, it smoothly goes from 0 to 1 and if we scale and translate it a bit we get this: -4x^^3^^/pi^^3^^ + 3x^^2^^/pi. Now we have nice sine-like curve in [-pi/2, pi/2] range. 

Almost all sine approximation code examples use conditional branching to reduce range of input parameter for use with approximating functions. You can reproduce it with few step functions and multiply-adds in shader code but there is a more clever way: see [[this article|http://realtimecollisiondetection.net/blog/?p=9]] by Christer Ericson.

#define M_PI 3.14159265

float curve( float x )
	const float a = -4./(M_PI*M_PI*M_PI);
	const float b = 3./M_PI;
	return x*( x*x*a + b );

float range_reduce( float angle )
	float x = mod( angle, M_PI*2. );
	x = min( x, M_PI - x );
	x = max( x, -M_PI - x );
	x = min( x, M_PI - x );
	return x;

float sin_like( float angle )
	return curve( range_reduce( angle ) );

float cos_like( float angle )
	return sin_like( angle + M_PI/2. );
And don't forget you can calculate 4 values 'for free' just by replacing float with vec4. 
My implementation of [[Linear-Speed Vertex Cache Optimisation|http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html]] algorithm by [[Tom Forsyth|http://www.eelpi.gotdns.org/blog.wiki.html]].

It requires [[boost|http://www.boost.org]], but all used libraries are header-only, so you only need to add it's path to your compiler includes, building isn't necessary.

Usage example:
#include "acmr.h"
#include "ls_vcache_opt.h"

    std::vector<unsigned short> pos_indices;
    std::vector<my_vertex> vertices;


    float acmr_before = ls_vcache_opt::acmr( &pos_indices[0], pos_indices.size()/3, 16 );
    ls_vcache_opt::optimize( &pos_indices[0], pos_indices.size()/3, vertices.size() );
    unsigned short num_verts = ls_vcache_opt::reorder_vertices( &pos_indices[0], 
        pos_indices.size()/3, vertices );
    vertices.resize( num_verts );
    float acmr_after = ls_vcache_opt::acmr( &pos_indices[0], pos_indices.size()/3, 16 );

[[download source code|code/ls_vcache_opt.zip]]
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|Description|select and extract tiddlers from your ~TiddlyWiki documents and save them to a separate file|
ExportTiddlersPlugin lets you select and extract tiddlers from your ~TiddlyWiki documents using interactive control panel lets you specify a destination, and then select which tiddlers to export. Tiddler data can be output as complete, stand-alone TiddlyWiki documents, or just the selected tiddlers ('~PureStore' format -- smaller files!) that can be imported directly into another ~TiddlyWiki, or as an ~RSS-compatible XML file that can be published for RSS syndication.
>see [[ExportTiddlersPluginInfo]]
!!!!!Inline control panel (live):
><<exportTiddlers inline>>
2008.09.29 [2.8.4] in getData(), convert existing TW file from UTF8 to Unicode before merging to correct handling of international characters and symbols.
|please see [[ExportTiddlersPluginInfo]] for additional revision details|
2005.10.09 [0.0.0] development started
// version
version.extensions.ExportTiddlersPlugin= {major: 2, minor: 8, revision: 4, date: new Date(2008,9,29)};

// default shadow definition
config.shadowTiddlers.ExportTiddlers='<<exportTiddlers inline>>';

// add 'export' backstage task (following built-in import task)
if (config.tasks) { // TW2.2 or above
	config.tasks.exportTask = {
		tooltip:'Export selected tiddlers to another file',
		content:'<<exportTiddlers inline>>'

// $(...) function: 'shorthand' convenience syntax for document.getElementById()
if (typeof($)=='undefined') { // avoid redefinition
function $() {
	var elements=new Array();
	for (var i=0; i<arguments.length; i++) {
		var element=arguments[i];
		if (typeof element=='string') element=document.getElementById(element);
		if (arguments.length==1) return element;
	return elements;

config.macros.exportTiddlers = {
	label: 'export tiddlers',
	prompt: 'Copy selected tiddlers to an export document',
	okmsg: '%0 tiddlers written to %1',
	failmsg: 'An error occurred while creating %1',
	mergeprompt: '%0\nalready contains tiddler definitions.\n'
		+'\nPress OK to add new/revised tiddlers to current file contents.'
		+'\nPress Cancel to completely replace file contents',
	mergestatus: 'Merged %0 new/revised tiddlers with %1 previously saved tiddlers',
	statusmsg: '%0 tiddler%1 - %2 selected for export',
	newdefault: 'export.html',
	datetimefmt: '0MM/0DD/YYYY 0hh:0mm:0ss',  // for 'filter date/time' edit fields
	type_TW: 'tw', type_PS: 'ps', type_TX: 'tx', type_NF: 'nf', // file type tokens
	type_map: { // map filetype param alternatives/abbreviations to token values
		tiddlywiki:'tw', tw:'tw', wiki: 'tw',
		purestore: 'ps', ps:'ps', store:'ps',
		plaintext: 'tx', tx:'tx', text: 'tx',
		newsfeed:  'nf', nf:'nf', xml:  'nf', rss:'nf'
	handler: function(place,macroName,params) {
		if (params[0]!='inline')
			{ createTiddlyButton(place,this.label,this.prompt,this.togglePanel); return; }
		var panel=this.createPanel(place);
	createPanel: function(place) {
		var panel=$('exportPanel');
		if (panel) { panel.parentNode.removeChild(panel); }
		var fn=$('exportFilename');
		if (window.location.protocol=='file:' && !fn.value.length) {
			// get new target path/filename
			var newPath=getLocalPath(window.location.href);
			var slashpos=newPath.lastIndexOf('/'); if (slashpos==-1) slashpos=newPath.lastIndexOf('\\'); 
			if (slashpos!=-1) newPath=newPath.substr(0,slashpos+1); // trim filename
		return panel;
	togglePanel: function(e) {
		if (!e) var e = window.event;
		var parent=resolveTarget(e).parentNode;
		var panel = $('exportPanel');
		if (panel==undefined || panel.parentNode!=parent)
		var isOpen = panel.style.display=='block';
			anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,'none'));
			panel.style.display = isOpen ? 'none' : 'block' ;
		if (panel.style.display!='none') { // update list and set focus when panel is made visible
			var fn=$('exportFilename'); fn.focus(); fn.select();
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	css: '\
		#exportPanel {\
			display: none; position:absolute; z-index:12; width:35em; right:105%; top:6em;\
			background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
			border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
			padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;\
		#exportPanel a, #exportPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
		#exportPanel table { \
			width:100%; border:0px; padding:0px; margin:0px;\
			font-size:8pt; line-height:110%; background:transparent;\
		#exportPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
		#exportPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
		#exportPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}\
		#exportPanel input  { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%; }\
		#exportPanel textarea  { width:98%;padding:0px;margin:0px;overflow:auto;font-size:8pt; }\
		#exportPanel .box { \
			border:1px solid black; padding:3px; margin-bottom:5px; \
			background:#f8f8f8; -moz-border-radius:5px;-webkit-border-radius:5px; }\
		#exportPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\
		#exportPanel .rad { width:auto;border:0 }\
		#exportPanel .chk { width:auto;border:0 }\
		#exportPanel .btn { width:auto; }\
		#exportPanel .btn1 { width:98%; }\
		#exportPanel .btn2 { width:48%; }\
		#exportPanel .btn3 { width:32%; }\
		#exportPanel .btn4 { width:24%; }\
		#exportPanel .btn5 { width:19%; }\
	html: '\
		<!-- target path/file  -->\
		export to path/filename:<br>\
		<input type="text" id="exportFilename" size=40 style="width:93%"><input \
			type="button" id="exportBrowse" value="..." title="select or enter a local folder/file..." style="width:5%" \
			onclick="var fn=config.macros.exportTiddlers.askForFilename(this); if (fn.length) this.previousSibling.value=fn; ">\
		<!-- output format -->\
		output file format:\
		<select id="exportFormat" size=1>\
			<option value="TW">TiddlyWiki HTML document (includes core code)</option>\
			<option value="PS">TiddlyWiki "PureStore" HTML file (tiddler data only)</option>\
			<option value="TX">TiddlyWiki plain text TXT file (tiddler source listing)</option>\
			<option value="NF">RSS NewsFeed XML file</option>\
		<!-- notes -->\
		<textarea id="exportNotes" rows=3 cols=40 style="height:4em;margin-bottom:5px;" onfocus="this.select()"></textarea> \
		<!-- list of tiddlers -->\
		<table><tr align="left"><td>\
			<a href="JavaScript:;" id="exportSelectAll"\
				onclick="config.macros.exportTiddlers.process(this)" title="select all tiddlers">\
			<a href="JavaScript:;" id="exportSelectChanges"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers changed since last save">\
				&nbsp;changes&nbsp;</a> \
			<a href="JavaScript:;" id="exportSelectOpened"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers currently being displayed">\
				&nbsp;opened&nbsp;</a> \
			<a href="JavaScript:;" id="exportSelectRelated"\
				onclick="config.macros.exportTiddlers.process(this)" title="select tiddlers related to the currently selected tiddlers">\
				&nbsp;related&nbsp;</a> \
			<a href="JavaScript:;" id="exportToggleFilter"\
				onclick="config.macros.exportTiddlers.process(this)" title="show/hide selection filter">\
				&nbsp;filter&nbsp;</a>  \
		</td><td align="right">\
			<a href="JavaScript:;" id="exportListSmaller"\
				onclick="config.macros.exportTiddlers.process(this)" title="reduce list size">\
			<a href="JavaScript:;" id="exportListLarger"\
				onclick="config.macros.exportTiddlers.process(this)" title="increase list size">\
		<select id="exportList" multiple size="10" style="margin-bottom:5px;"\
		<!-- selection filter -->\
		<div id="exportFilterPanel" style="display:none">\
		<table><tr align="left"><td>\
			selection filter\
		</td><td align="right">\
			<a href="JavaScript:;" id="exportHideFilter"\
				onclick="config.macros.exportTiddlers.process(this)" title="hide selection filter">hide</a>\
		<div class="box">\
		<input type="checkbox" class="chk" id="exportFilterStart" value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> starting date/time<br>\
		<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
			<select size=1 id="exportFilterStartBy" \
				<option value="0">today</option>\
				<option value="1">yesterday</option>\
				<option value="7">a week ago</option>\
				<option value="30">a month ago</option>\
				<option value="file">file date</option>\
				<option value="other">other (mm/dd/yyyy hh:mm)</option>\
		</td><td width="50%">\
			<input type="text" id="exportStartDate" onfocus="this.select()"\
		<input type="checkbox" class="chk" id="exportFilterEnd" value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> ending date/time<br>\
		<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
			<select size=1 id="exportFilterEndBy" \
				<option value="0">today</option>\
				<option value="1">yesterday</option>\
				<option value="7">a week ago</option>\
				<option value="30">a month ago</option>\
				<option value="file">file date</option>\
				<option value="other">other (mm/dd/yyyy hh:mm)</option>\
		</td><td width="50%">\
			<input type="text" id="exportEndDate" onfocus="this.select()"\
		<input type="checkbox" class="chk" id=exportFilterTags value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> match tags<br>\
		<input type="text" id="exportTags" onfocus="this.select()">\
		<input type="checkbox" class="chk" id=exportFilterText value="1"\
			onclick="config.macros.exportTiddlers.showFilterFields(this)"> match titles/tiddler text<br>\
		<input type="text" id="exportText" onfocus="this.select()">\
		</div> <!--box-->\
		</div> <!--panel-->\
		<!-- action buttons -->\
		<div style="text-align:center">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportFilter" value="apply filter">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportStart" value="export tiddlers">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportDelete" value="delete tiddlers">\
		<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"\
			id="exportClose" value="close">\
	process: function(which) { // process panel control interactions
		// DEBUG alert(which.id);
		var theList=$('exportList'); if (!theList) return;
		var count = 0;
		var total = store.getTiddlers('title').length;
		switch (which.id) {
			case 'exportFilter':
				var panel=$('exportFilterPanel');
				if (count==-1) { panel.style.display='block'; break; }
				if (count==0) { alert('No tiddlers were selected'); panel.style.display='block'; }
			case 'exportStart':
			case 'exportDelete':
			case 'exportHideFilter':
			case 'exportToggleFilter':
				var panel=$('exportFilterPanel')
			case 'exportSelectChanges':
				var lastmod=new Date(document.lastModified);
				for (var t = 0; t < theList.options.length; t++) {
					if (theList.options[t].value=='') continue;
					var tiddler=store.getTiddler(theList.options[t].value); if (!tiddler) continue;
					count += (tiddler.modified>lastmod)?1:0;
				if (count==0) alert('There are no unsaved changes');
			case 'exportSelectAll':
				for (var t = 0; t < theList.options.length; t++) {
					if (theList.options[t].value=='') continue;
					count += 1;
			case 'exportSelectOpened':
				for (var t = 0; t < theList.options.length; t++) theList.options[t].selected=false;
				var tiddlerDisplay = $('tiddlerDisplay'); // for TW2.1-
				if (!tiddlerDisplay) tiddlerDisplay = $('storyDisplay'); // for TW2.2+
				for (var t=0;t<tiddlerDisplay.childNodes.length;t++) {
					var tiddler=tiddlerDisplay.childNodes[t].id.substr(7);
					for (var i = 0; i < theList.options.length; i++) {
						if (theList.options[i].value!=tiddler) continue;
						theList.options[i].selected=true; count++; break;
				if (count==0) alert('There are no tiddlers currently opened');
			case 'exportSelectRelated':
				// recursively build list of related tiddlers
				function getRelatedTiddlers(tid,tids) {
					var t=store.getTiddler(tid); if (!t || tids.contains(tid)) return tids;
					if (!t.linksUpdated) t.changed();
					for (var i=0; i<t.links.length; i++)
						if (t.links[i]!=tid) tids=getRelatedTiddlers(t.links[i],tids);
					return tids;
				// for all currently selected tiddlers, gather up the related tiddlers (including self) and select them as well
				var tids=[];
				for (var i=0; i<theList.options.length; i++)
					if (theList.options[i].selected) tids=getRelatedTiddlers(theList.options[i].value,tids);
				// select related tiddlers (includes original selected tiddlers)
				for (var i=0; i<theList.options.length; i++)
			case 'exportListSmaller':	// decrease current listbox size
				var min=5;
			case 'exportListLarger':	// increase current listbox size
				var max=(theList.options.length>25)?theList.options.length:25;
			case 'exportClose':
	displayStatus: function(count,total) {
		var txt=this.statusmsg.format([total,total!=1?'s':'',!count?'none':count==total?'all':count]);
		clearMessage();	displayMessage(txt);
		return txt;
	refreshList: function(selectedIndex) {
		var theList = $('exportList'); if (!theList) return;
		// get the sort order
		var sort;
		if (!selectedIndex)   selectedIndex=0;
		if (selectedIndex==0) sort='modified';
		if (selectedIndex==1) sort='title';
		if (selectedIndex==2) sort='modified';
		if (selectedIndex==3) sort='modifier';
		if (selectedIndex==4) sort='tags';

		// unselect headings and count number of tiddlers actually selected
		var count=0;
		for (var t=5; t < theList.options.length; t++) {
			if (!theList.options[t].selected) continue;
			if (theList.options[t].value!='')
			else { // if heading is selected, deselect it, and then select and count all in section
				for ( t++; t<theList.options.length && theList.options[t].value!=''; t++) {

		// disable 'export' and 'delete' buttons if no tiddlers selected

		// show selection count
		var tiddlers = store.getTiddlers('title');
		if (theList.options.length) this.displayStatus(count,tiddlers.length);

		// if a [command] item, reload list... otherwise, no further refresh needed
		if (selectedIndex>4) return;

		// clear current list contents
		while (theList.length > 0) { theList.options[0] = null; }
		// add heading and control items to list
		var i=0;
		var indent=String.fromCharCode(160)+String.fromCharCode(160);
			new Option(tiddlers.length+' tiddlers in document', '',false,false);
			new Option(((sort=='title'   )?'>':indent)+' [by title]', '',false,false);
			new Option(((sort=='modified')?'>':indent)+' [by date]', '',false,false);
			new Option(((sort=='modifier')?'>':indent)+' [by author]', '',false,false);
			new Option(((sort=='tags'    )?'>':indent)+' [by tags]', '',false,false);

		// output the tiddler list
		switch(sort) {
			case 'title':
				for(var t = 0; t < tiddlers.length; t++)
					theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
			case 'modifier':
			case 'modified':
				var tiddlers = store.getTiddlers(sort);
				// sort descending for newest date first
				tiddlers.sort(function (a,b) {if(a[sort] == b[sort]) return(0); else return (a[sort] > b[sort]) ? -1 : +1; });
				var lastSection = '';
				for(var t = 0; t < tiddlers.length; t++) {
					var tiddler = tiddlers[t];
					var theSection = '';
					if (sort=='modified') theSection=tiddler.modified.toLocaleDateString();
					if (sort=='modifier') theSection=tiddler.modifier;
					if (theSection != lastSection) {
						theList.options[i++] = new Option(theSection,'',false,false);
						lastSection = theSection;
					theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
			case 'tags':
				var theTitles = {}; // all tiddler titles, hash indexed by tag value
				var theTags = new Array();
				for(var t=0; t<tiddlers.length; t++) {
					var title=tiddlers[t].title;
					var tags=tiddlers[t].tags;
					if (!tags || !tags.length) {
						if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
					else for(var s=0; s<tags.length; s++) {
						if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
				for(var tagindex=0; tagindex<theTags.length; tagindex++) {
					var theTag=theTags[tagindex];
					theList.options[i++]=new Option(theTag,'',false,false);
					for(var t=0; t<theTitles[theTag].length; t++)
						theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
		theList.selectedIndex=selectedIndex; // select current control item
	askForFilename: function(here) {
		var msg=here.title; // use tooltip as dialog box message
		var path=getLocalPath(document.location.href);
		var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\'); 
		if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
		var filetype=$('exportFormat').value.toLowerCase();
		var defext='html';
		if (filetype==this.type_TX) defext='txt';
		if (filetype==this.type_NF) defext='xml';
		var file=this.newdefault.replace(/html$/,defext);
		var result='';
		if(window.Components) { // moz
			try {
				var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
				var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
				picker.init(window, msg, nsIFilePicker.modeSave);
				var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
				if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
			catch(e) { alert('error during local file access: '+e.toString()) }
		else { // IE
			try { // XPSP2 IE only
				var s = new ActiveXObject('UserAccounts.CommonDialog');
				s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|XML files|*.xml|';
				if (s.showOpen()) var result=s.FileName;
			catch(e) {  // fallback
				var result=prompt(msg,path+file);
		return result;
	initFilter: function() {
		// start date
		// end date
		// tags
		// text
		// show/hide filter input fields
	showFilterFields: function(which) {
		var show=$('exportFilterStart').checked;
		var val=$('exportFilterStartBy').value;
		if (which && (which.id=='exportFilterStartBy') && (val=='other'))

		var show=$('exportFilterEnd').checked;
		var val=$('exportFilterEndBy').value;
		 if (which && (which.id=='exportFilterEndBy') && (val=='other'))

		var show=$('exportFilterTags').checked;

		var show=$('exportFilterText').checked;
	getFilterDate: function(val,id) {
		var result=0;
		switch (val) {
			case 'file':
				result=new Date(document.lastModified);
			case 'other':
				result=new Date($(id).value);
			default: // today=0, yesterday=1, one week=7, two weeks=14, a month=31
				var now=new Date(); var tz=now.getTimezoneOffset()*60000; now-=tz;
				var oneday=86400000;
				if (id=='exportStartDate')
					result=new Date((Math.floor(now/oneday)-val)*oneday+tz);
					result=new Date((Math.floor(now/oneday)-val+1)*oneday+tz-1);
		return result;
	filterExportList: function() {
		var theList  = $('exportList'); if (!theList) return -1;
		var filterStart=$('exportFilterStart').checked;
		var val=$('exportFilterStartBy').value;
		var startDate=config.macros.exportTiddlers.getFilterDate(val,'exportStartDate');
		var filterEnd=$('exportFilterEnd').checked;
		var val=$('exportFilterEndBy').value;
		var endDate=config.macros.exportTiddlers.getFilterDate(val,'exportEndDate');
		var filterTags=$('exportFilterTags').checked;
		var tags=$('exportTags').value;
		var filterText=$('exportFilterText').checked;
		var text=$('exportText').value;
		if (!(filterStart||filterEnd||filterTags||filterText)) {
			alert('Please set the selection filter');
			return -1;
		if (filterStart&&filterEnd&&(startDate>endDate)) {
			var msg='starting date/time:\n'
			msg+='is later than ending date/time:\n'
			return -1;
		// if filter by tags, get list of matching tiddlers
		// use getMatchingTiddlers() (if MatchTagsPlugin is installed) for full boolean expressions
		// otherwise use getTaggedTiddlers() for simple tag matching
		if (filterTags) {
			var fn=store.getMatchingTiddlers||store.getTaggedTiddlers;
			var t=fn.apply(store,[tags]);
			var tagged=[];
			for (var i=0; i<t.length; i++) tagged.push(t[i].title);
		// scan list and select tiddlers that match all applicable criteria
		var total=0;
		var count=0;
		for (var i=0; i<theList.options.length; i++) {
			// get item, skip non-tiddler list items (section headings)
			var opt=theList.options[i]; if (opt.value=='') continue;
			// get tiddler, skip missing tiddlers (this should NOT happen)
			var tiddler=store.getTiddler(opt.value); if (!tiddler) continue; 
			var sel=true;
			if ( (filterStart && tiddler.modified<startDate)
			|| (filterEnd && tiddler.modified>endDate)
			|| (filterTags && !tagged.contains(tiddler.title))
			|| (filterText && (tiddler.text.indexOf(text)==-1) && (tiddler.title.indexOf(text)==-1)))
		return count;
	deleteTiddlers: function() {
		var list=$('exportList'); if (!list) return;
		var tids=[];
		for (i=0;i<list.length;i++)
			if (list.options[i].selected && list.options[i].value.length)
		if (!confirm('Are you sure you want to delete these tiddlers:\n\n'+tids.join(', '))) return;
		for (t=0;t<tids.length;t++) {
			var tid=store.getTiddler(tids[t]); if (!tid) continue;
			var msg="'"+tid.title+"' is tagged with 'systemConfig'.\n\n";
			msg+='Removing this tiddler may cause unexpected results.  Are you sure?'
			if (tid.tags.contains('systemConfig') && !confirm(msg)) continue;
		alert(tids.length+' tiddlers deleted');
		this.refreshList(0); // reload listbox
		store.notifyAll(); // update page display
	go: function() {
		if (window.location.protocol!='file:') // make sure we are local
			{ displayMessage(config.messages.notFileUrlError); return; }
		// get selected tidders, target filename, target type, and notes
		var list=$('exportList'); if (!list) return;
		var tids=[]; for (var i=0; i<list.options.length; i++) {
			var opt=list.options[i]; if (!opt.selected||!opt.value.length) continue;
			var tid=store.getTiddler(opt.value); if (!tid) continue;
		if (!tids.length) return; // no tiddlers selected
		var target = $('exportFilename').value.trim();
		if (!target.length) {
			displayMessage('A local target path/filename is required',target);
		var filetype = $('exportFormat').value.toLowerCase();
		var notes=$('exportNotes').value.replace(/\n/g,'<br>');
		var total={val:0};
		var out=this.assembleFile(target,filetype,tids,notes,total);
		var link='file:///'+target.replace(/\\/g,'/');
		var samefile=link==decodeURIComponent(window.location.href);
		var p=getLocalPath(document.location.href);
		if (samefile) {
			if (config.options.chkSaveBackups) { var t=loadOriginal(p);if(t)saveBackup(p,t); }
			if (config.options.chkGenerateAnRssFeed && saveRss instanceof Function) saveRss(p);
		var ok=saveFile(target,out);
		 '// Source'+':\n//\t%0\n'
		+'// Title:\n//\t%1\n'
		+'// Subtitle:\n//\t%2\n'
		+'// Created:\n//\t%3 by %4\n'
		+'// Application:\n//\tTiddlyWiki %5 / %6 %7\n',
		'\n// ----- %0 (by %1 on %2) -----\n\n%3',
		 '<'+'?xml version="1.0"?'+'>\n'
		+'<rss version="2.0">\n'
		+'<copyright>Copyright '+(new Date().getFullYear())+' %4</copyright>\n'
		+'<generator>TiddlyWiki %5 / %6 %7</generator>\n',
		+'<style type="text/css">'
		+'	#storeArea {display:block;margin:1em;}'
		+'	#storeArea div {padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}'
		+'	#pureStoreHeading {width:100%;text-align:left;background-color:#eeeeee;padding:1em;}'
		+'<div id="pureStoreHeading">'
		+'	TiddlyWiki "PureStore" export file<br>'
		+'	Source'+': <b>%0</b><br>'
		+'	Title: <b>%1</b><br>'
		+'	Subtitle: <b>%2</b><br>'
		+'	Created: <b>%3</b> by <b>%4</b><br>'
		+'	TiddlyWiki %5 / %6 %7<br>'
		+'	Notes:<hr><pre>%8</pre>'
		+'<div id="storeArea">',
	assembleFile: function(target,filetype,tids,notes,total) {
		var revised='';
		var now = new Date().toLocaleString();
		var src=convertUnicodeToUTF8(document.location.href);
		var title = convertUnicodeToUTF8(wikifyPlain('SiteTitle').htmlEncode());
		var subtitle = convertUnicodeToUTF8(wikifyPlain('SiteSubtitle').htmlEncode());
		var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
		var twver = version.major+'.'+version.minor+'.'+version.revision;
		var v=version.extensions.ExportTiddlersPlugin; var pver = v.major+'.'+v.minor+'.'+v.revision;
		var headerargs=[src,title,subtitle,now,user,twver,'ExportTiddlersPlugin',pver,notes];
		switch (filetype) {
			case this.type_TX: // plain text
				var header=this.plainTextHeader.format(headerargs);
				var footer=this.plainTextFooter;
			case this.type_NF: // news feed (XML)
				var header=this.newsFeedHeader.format(headerargs);
				var footer=this.newsFeedFooter;
			case this.type_PS: // PureStore (no code)
				var header=this.pureStoreHeader.format(headerargs);
				var footer=this.pureStoreFooter;
			case this.type_TW: // full TiddlyWiki
				var currPath=getLocalPath(window.location.href);
				var original=loadFile(currPath);
				if (!original) { displayMessage(config.messages.cantSaveError); return; }
				var posDiv = locateStoreArea(original);
				if (!posDiv) { displayMessage(config.messages.invalidFileError.format([currPath])); return; }
				var header = original.substr(0,posDiv[0]+startSaveArea.length)+'\n';
				var footer = '\n'+original.substr(posDiv[1]);
		var out=this.getData(target,filetype,tids);
		var revised = header+convertUnicodeToUTF8(out.join('\n'))+footer;
		// if full TW, insert page title and language attr, and reset all MARKUP blocks...
		if (filetype==this.type_TW) {
			var newSiteTitle=convertUnicodeToUTF8(getPageTitle()).htmlEncode();
			revised=revised.replaceChunk('<title'+'>','</title'+'>',' ' + newSiteTitle + ' ');
			var titles=[]; for (var i=0; i<tids.length; i++) titles.push(tids[i].title);
				titles.contains('MarkupPreHead')? 'MarkupPreHead' :null);
				titles.contains('MarkupPreBody')? 'MarkupPreBody' :null);
		return revised;
	formatItem: function(s,f,t,u) {
		if (f==this.type_TW)
			var r=s.getSaver().externalizeTiddler(s,t);
		if (f==this.type_PS)
			var r=config.macros.exportTiddlers.pureStoreTiddler.format([t.title,s.getSaver().externalizeTiddler(s,t)]);
		if (f==this.type_NF)
			var r=this.newsFeedTiddler.format([t.saveToRss(u)]);
		if (f==this.type_TX)
			var r=this.plainTextTiddler.format([t.title,t.modifier,t.modified.toLocaleString(),t.text]);
		return r||'';
	getData: function(target,filetype,tids) {
		// output selected tiddlers and gather list of titles (for use with merge)
		var out=[]; var titles=[];
		var url=store.getTiddlerText('SiteUrl','');
		for (var i=0; i<tids.length; i++) {
		// if TW or PureStore format, ask to merge with existing tiddlers (if any)
		if (filetype==this.type_TW || filetype==this.type_PS) {
			var text=loadFile(target);
			if (text && text.length) {
				var remoteStore=new TiddlyWiki();
				if (remoteStore.importTiddlyWiki(convertUTF8ToUnicode(text))
					&& confirm(this.mergeprompt.format([target]))) {
					var existing=remoteStore.getTiddlers('title');
					for (var i=0; i<existing.length; i++)
						if (!titles.contains(existing[i].title))
		return out;
[[About]] [[Products|http://www.rusteddreams.net]] [[RSS Feed|index.xml]]
border: none;

font: 0.8em Verdana, Geneva, Arial, Helvetica, sans-serif;
color: #fff;
background-color: #333;
width: 900px;
margin: 16px auto;
text-align: left;
width: 100%;
height: 80px;
background: transparent;
font-size: 7pt;
padding: 5px 0 0 75px;
float: left;
margin: 0;
font-family: 'NP Naipol All in One', Verdana, Helvetica, sans-serif;
font-weight: bold;
font-size: 30px;
color: #4a3509;
background: url(images/corner_tl.gif)  no-repeat top left;
float: right;
margin: 0;
background: url(images/corner_tr.gif)  no-repeat top right;
#topMenu a{
float: left;
width: 75px;
padding: 5px 10px 5px 10px;
margin: 0;
font-size: 7pt;
font-weight: normal;
text-align: center;
text-transform: uppercase;
text-decoration: none;
color: #4a3509;
background: transparent;
border-bottom: 2px solid #333;
border-left: 2px solid #333;
#topMenu a:hover{
color: #fc0;
background-color: #333;

float: left;
width: 100%;
padding: 0 0 16px 0;
background-color: #666;
line-height: 1.8em;
#page a{
text-decoration: none;
color: #fc0;
border-bottom: 1px solid #aaa;
#page a:hover{
color: #ddd;
background-color: #888;
#page .highlight, #page .marked{
background: #a7a13B;
text-decoration: underline;
position: static;
overflow-x: hidden;
float: right;
width: 200px;
background: #777 url(images/corner_sub_tl.gif) no-repeat top left;
margin: 16px 0 0 0;
#sidebarOptions, #sidebarTabs{
margin: 0.96em 0.96em 0 0.96em;
#sidebar a{
border: none;
#sidebarTabs .tabContents{
width: 100%;
padding-left: 0;
padding-right: 0;
background: #777;
color: #fff;
border: none;
border-top: 1px solid #888;
#sidebarTabs .tabSelected, #sidebarTabs .txtMoreTab .tabSelected{
background: #666;
border:1px solid #888;
#sidebarTabs .tabUnselected{
background: #777;
border:1px solid #888;
color: #bba13b;

background: #777 url(images/corner_sub_bl.gif) no-repeat bottom left;
height: 10px;
margin: 0;
float: left;
width: 686px;
margin: 16px 0 0 0;
background: #777;
background: url(images/corner_sub_tr.gif) no-repeat top right;
height: 10px;
padding:0 0.96em 0 0.96em;
font-size: 1em;
margin-bottom: 1.8em;
font-size: 0.8em;
margin-top: 0;
float: right;
#page .toolbar a{
border: none;
padding: 0 0.6em 0 0.6em;
color: #777;
#page .selected .toolbar a{
color: #aaa;
font-weight: bold;
font-size: 0.8em;
text-transform: lowercase;
color: #fc0;
border-bottom: 1px solid #aaa;
margin-top: 0;
#page .subtitle, #page .subtitle a{
color: #aaa;
border: none;
font-size: 0.8em;
#page .tagging, #page .tagged, #page .selected .tagging, #page .selected .tagged{
border: 1px solid #888;
background: #555;
#page .tagging .listTitle, #page .tagged .listTitle, #page .selected .tagging .listTitle, #page .selected .tagged .listTitle{
color: #aaa;
#page .tagging .button, #page .tagged .button, #page .selected .tagging .button, #page .selected .tagged .button{
color:	 #888;
background: #555;
border: none;
#page .tagging .button:hover, #page .tagged .button:hover, #page .tagging .button:active, #page .tagged .button:active{
border: none;
#page .viewer .button, #page .editorFooter .button{
color: #fc0;
border: 1px solid #888;
background: #555;
margin: 0 0.4em;
#page .viewer .button:hover, #page .editorFooter .button:hover, #page .viewer .button:active, #page .viewer .highlight, #page .editorFooter .button:active, #page .editorFooter .highlight{
color: #ddd;
background: #888;
border-color: #aaa;
background: url(images/corner_sub_br.gif) no-repeat bottom right;
height: 10px;

background: #333;
border: 1px solid #444;
.popup li.disabled{
color: #000;
.popup li a, .popup li a:visited{
color: #777;
border: none;
.popup li a:hover{
background: #333;
color: #ccc;
border: none;
.popup hr{
color: #777;
background: #777;
border-bottom: 1px;
.listBreak div{
border-bottom: 1px solid #777;

border: 1px solid #ccc;
background: #555;
color: #777;
#messageArea .button{
color: #333;
border: 1px solid #ccc;
#messageArea .button:hover{
color: #ccc;
background: #333;
border-color: #333;

.viewer table, .viewer td{
border: 1px solid #888;
font-size: 0.95em;
.viewer th, thead td{
background: #555;
border: 1px solid #888;
color: #fc0;
font-weight: normal;
.viewer pre, .viewer blockquote{
border: 1px solid #888;
background: #555;
font-size: 0.95em;
.viewer code{
color: #ccc; 
.viewer hr{
border-top: dashed 1px #222; 
margin: 0 1em;
.editor input{
border: 1px solid #ccc; 
.editor textarea{
border: 1px solid #ccc;

width: 100%;
float: left;
font-size: 7pt;
text-transform: lowercase;
line-height: 2.6em;
color: #4a3509;
background: transparent;
float: left;
width: 60%;
padding-left: 16px;
text-align: left;
background: url(images/corner_bl.gif) no-repeat bottom left;
float: right;
width: 35%;
text-align: right;
padding-right: 16px;
background: url(images/corner_br.gif) no-repeat bottom right;
#footer a{
color: #4a3509;
#footer a:hover{
color: #666;
clear: both;
<div id='site'>
	<div id='header' class='header' macro='gradient horiz #fc0 #777'>
		<div id='siteTitle' refresh='content' tiddler='SiteTitle'></div>
		<span id='topMenu' refresh='content' tiddler='MainMenu'></span>
	<div id=page>
		<div id='sidebar'>
			<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
			<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
			<div id='sidebarEnd'></div>
		<div id='displayArea'>
			<div id='messageArea'></div>
			<div id='tiddlersStart'></div>
			<div id='tiddlerDisplay'></div>
			<div id='tiddlersEnd'></div>
	<div id='footer' macro='gradient horiz #fc0 #777'>
		<div id='footerLeft'>&copy; 2008 rusted dreams</div>
		<div id='footerRight' refresh='content' tiddler='SiteSubtitle'>;)</div>
	<div class="clearer"></div>
<<search>><<closeAll>><<permaview>><<newTiddler>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
rusted dreams
graphics programming
I'm Ryhor Spivak, author of [[rusted dreams|http://www.rusteddreams.net]] projects, and this page is a collection of my graphics programming related code snippets, algorithm ideas and some random thoughts.

If you have any comments you can mail me: grisha ~~at~~ rusteddreams ~~dot~~ net
~~'Grisha' is another way to write my name, so don't be confused then you see it in code comments or other parts of site.~~
// //''Name:'' Blog
// //''Version:'' 0.1.1
// //''Author:'' [[Anshul Nigham|http://yavin4.anshul.info]] (adapted from an earlier plugin by [[ClintChecketts|http://www.checkettsweb.com/]]) 
// //Tag support given by [[Emmanuel Frécon|http://www.sics.se/~emmanuel/]]
// //''Type:'' Plugin
// //''Description:'' Posts the most recently edited tiddlers when the TiddlyWiki is opened, similar to a blog.
// //''Syntax:'' Change the daysOrPosts and numOfDaysOrPosts variables below
// // If daysOrPosts variable is "days", tiddlers from the past numOfDaysOrPosts dates will be displayed
// // If daysOrPosts variable is "posts", the past numOfDaysOrPosts tiddlers will be displayed 
// // The restrictTag variable contains the tag that all entries should contain
// // for appearing as blog entries. Other entries will not appear, except for
// // those coming from the the defaultTiddlers. If the variable is an empty
// // string, then the behaviour is the same version 0.1 (below).

// // ''Tested against:'' Tiddlywiki 2.1.3

// // ''HOWTO:'' Simply copy this entire tiddler and paste it into a new tiddler in your own tiddlywiki.
// // Tag it with systemConfig, and also with systemTiddlers if you don't want it to appear within the blog views. Name it anything you like

var daysOrPosts = "posts";
var numOfDaysOrPosts = "10";
var restrictTag = "blogentry";

function displayTopTiddlers()
	if(window.location.hash) daysOrPosts = "";
	if(daysOrPosts == "posts")
		var tiddlerNames = store.reverseLookup("tags","systemTiddlers",false,"modified");
		if (tiddlerNames.length < numOfDaysOrPosts)
			numOfDaysOrPosts = tiddlerNames.length;
		for(var t = tiddlerNames.length-numOfDaysOrPosts;t<=tiddlerNames.length-1;t++)
      if (restrictTag == "" || tiddlerNames[t].isTagged(restrictTag))
	if (daysOrPosts == "days"){
		var lastDay = "";
		var tiddlerNames = store.reverseLookup("tags","systemTiddlers",false,"modified");
		var t = tiddlerNames.length -1;
		var tFollower = 0;
		for(t;t>=0;t--) if(numOfDaysOrPosts >= 0){
			var theDay = tiddlerNames[t].modified.convertToYYYYMMDDHHMM().substr(0,8);
			if(theDay != lastDay){
				numOfDaysOrPosts = numOfDaysOrPosts -1;
				lastDay = theDay;
				tFollower = t;

		for(tFollower = tFollower+1; tFollower < tiddlerNames.length;tFollower++){
      if (restrictTag == "" || tiddlerNames[tFollower].isTagged(restrictTag))


window.original_restart = window.restart;
window.restart = function()