<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Adventures in Keyframes and Code</title>
	<atom:link href="http://www.keyframesandcode.com/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.keyframesandcode.com/code</link>
	<description>MaxScript, ActionScript, PHP &#38; JavaScript</description>
	<pubDate>Tue, 30 Sep 2008 08:29:10 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Kohana: Default controller for serving static pages</title>
		<link>http://www.keyframesandcode.com/code/development/php/kohana/kohana-default-controller-for-serving-static-pages/</link>
		<comments>http://www.keyframesandcode.com/code/development/php/kohana/kohana-default-controller-for-serving-static-pages/#comments</comments>
		<pubDate>Thu, 11 Sep 2008 14:28:03 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Kohana]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/?p=199</guid>
		<description><![CDATA[A brief overview of the problem
A framework such as Kohana is really useful when serving lots of dynamic pages, as it allows you organise both your thoughts and your code into folders, creating order from what could potentially be chaos, at the expense of having to adhere to a structured way of working, in this [...]]]></description>
			<content:encoded><![CDATA[<h3>A brief overview of the problem</h3>
<p>A framework such as Kohana is really useful when serving lots of dynamic pages, as it allows you organise both your thoughts and your code into folders, creating order from what could potentially be chaos, at the expense of having to adhere to a structured way of working, in this case setting up controllers and  methods that are correctly mapped from URLs (routes).</p>
<p>Whilst this is necessary for dynamic pages that need access to models, helpers and such like, for static pages that literally just need to be output to the browser, it&#8217;s quite a lot of overhead, and your controllers can very quickly end up bloated with itty-bitty &#8220;view this page&#8221; methods.</p>
<p>What would be great would be some magic method to automatically handle static pages, without having to set up an actual controller and method, that way all you really need to create is the view file, and you leave your controllers folder nice and lean.</p>
<p><em>Note: this tutorial is for Kohana 2.2. I haven&#8217;t investigated how it will work with 2.3 and the routing yet. Please comment if you have something to add!</em></p>
<h3>The Fallback controller</h3>
<p>The solution is using a default or &#8220;fallback&#8221; controller that takes care of serving views from routes that don&#8217;t map to an existing controller and method.</p>
<p>Essentially, this new controller kicks in just after the routing has drawn a blank, locates the appropriate view file, then serves it - instead of serving a 404 page.</p>
<h3>Step-by-step</h3>
<p>Let&#8217;s a take a look at how this works in a step-by-step approach from Kohana&#8217;s perspective. Don&#8217;t worry - it&#8217;s actually pretty simple, I&#8217;ve just mapped it out specifically so you can see what&#8217;s happening every step of the way.</p>
<p><strong>Let&#8217;s start with a route</strong></p>
<ul>
<li>The user navigates to a route, lets say <strong>/about/company/history</strong></li>
<li>Kohana looks for
<ul>
<li>an <strong>about</strong> controller, with</li>
<li>a <strong>company</strong> method, and intends to supply it with</li>
<li>a <strong>history</strong> argument</li>
</ul>
</li>
</ul>
<p>Here&#8217;s the thing though, because we did&#8217;t want the hassle of setting up ALL our controllers with methods just to load views, our <strong>about</strong> controller doesn&#8217;t have a <strong>company</strong> method! So what is Kohana to do?</p>
<p><strong>Let&#8217;s load the Fallback Controller!</strong><br />
Well, normally, a 404 page would be served, but using the magic of <a href="http://docs.kohanaphp.com/general/hooks">hooks</a>, we&#8217;re going to give Kohana one last chance to do something before serving that horrid 404 page.</p>
<p>So:</p>
<ul>
<li>The <a href="http://docs.kohanaphp.com/general/events#system.post_routing">system.post_routing</a> hook kicks in, and due to the way we&#8217;ve set up the hook file</li>
<li>It loads our <strong>Fallback controller</strong>, which is designed to do something useful with the route before bombing out.</li>
</ul>
<p><strong>Let&#8217;s have the Fallback Controller do it&#8217;s stuff and find the view!</strong><br />
In this case, we want it to attempt to find a view, so:</p>
<ul>
<li>The Fallback controller builds a path from the arguments <strong>/about</strong> that will (hopefully) map to a view, in this case <strong>views/main/about.php</strong>, then</li>
<li>Attempts to find this view using <a href="http://docs.kohanaphp.com/core/kohana#finding_files">Kohana::find_file</a>, and
<ul>
<li>if found - <strong>loads the view!</strong></li>
<li>if not found - serves the 404 page</li>
</ul>
</li>
</ul>
<h3>Summary</h3>
<p>Pretty useful, huh? Basically all that happened is</p>
<ul>
<li>Kohana couldn&#8217;t find a controller, so loaded a Fallback controller</li>
<li>The fallback controller took the route and found a view</li>
<li>Kohana loads the view (or the 404 page if the view didn&#8217;t exist)</li>
</ul>
<p>OK, perhaps it&#8217;s time to look at some code</p>
<h3>Code</h3>
<p>You will need to create or edit code in 3 separate places:</p>
<ol>
<li>Enable hooks in your application&#8217;s configuration file</li>
<li>Create a hook file which tells Kohana what to do in the system.post_routing event</li>
<li>Create the actual Fallback Controller that will do the finding and loading of your view files</li>
</ol>
<p>.</p>
<p><strong>application/config/config.php</strong> - Enable hooks in the main config file&#8230;</p>
<pre>$config['enable_hooks'] = TRUE;</pre>
<p>.</p>
<p><strong>application/hooks/fallback_page.php</strong> - Add a hook file to the hooks directory&#8230;</p>
<pre>Event::add('system.post_routing' ,'call_fallback_page');

function call_fallback_page()
{
    if (Router::$controller === NULL)
    {
        Router::$controller = 'fallback_page';
        Router::$arguments = Router::$segments;
        Router::$controller_path = APPPATH.'controllers/fallback_page.php';
    }
}</pre>
<p>.</p>
<p><strong>application/controllers/fallback_page.php</strong> - Set up a controller to do the business</p>
<pre>class Fallback_Page_Controller extends Page_Controller // page controller is my standard page template
{

    public function __construct()
    {

    // the constructor can be omitted if you're not doing anything special,
    // as the parent constructor it is called automatically
        parent::__construct();
    }

    public function __call($function, $arguments)
    {

    // search for a view, and load it if it exists
    // it's up to you how you handle the mapping of arguments to folders here!
        $route =  implode('/', $arguments);
        if(Kohana::find_file('views/main', $route))
        {
            $this-&gt;template-&gt;content = new View('main/'.$route);
        }

    // display alternative content if not found
        else
        {
            $view = new View('common/404');
            $view-&gt;info = array($function, $arguments);
            $this-&gt;template-&gt;content = $view;
        }
    }

}</pre>
<h3>Conculsion</h3>
<p>So now you should be able to set up a bunch of views, but needn&#8217;t worry about building controllers for them if nothing exciting is happening.</p>
<p>The fallback controller will run, will attempt to find a matching view, and if found, will echo it the page. The main thing is - it keeps your controllers folder and classes nice and lean, and you only need to build controllers that actually DO any controlling!</p>
<p>Nice.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/php/kohana/kohana-default-controller-for-serving-static-pages/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Kohana &#8216;filesystem&#8217; Helper</title>
		<link>http://www.keyframesandcode.com/code/development/kohana-filesystem-helper/</link>
		<comments>http://www.keyframesandcode.com/code/development/kohana-filesystem-helper/#comments</comments>
		<pubDate>Wed, 20 Aug 2008 12:49:24 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Kohana]]></category>

		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/?p=198</guid>
		<description><![CDATA[Kohana is just an excellent framework to work with, but one of my beefs is that the structure of its bundled libraries and helpers is a little haphazard, missing basic functions you&#8217;d expect to find in the core (so you can rely on them when writing modules) omitted!
For this reason, here&#8217;s my attempt at a [...]]]></description>
			<content:encoded><![CDATA[<p>Kohana is just an excellent framework to work with, but one of my beefs is that the structure of its bundled libraries and helpers is a little haphazard, missing basic functions you&#8217;d expect to find in the core (so you can rely on them when writing modules) omitted!</p>
<p>For this reason, here&#8217;s my attempt at a more integrated helper class. It&#8217;s somewhat of a Frankenstein&#8217;s monster with bits borrowed from Code Igniter, and my own code thrown in as well. Apart from this, it:</p>
<ul>
<li>Adds a few new methods</li>
<li>Improves on some existing methods</li>
<li>Combines the &#8216;download&#8217; helper functionality</li>
<li>Tidies up various method calls</li>
</ul>
<p>The methods are:</p>
<ul>
<li>map()</li>
<li>get_folders()</li>
<li>get_files()</li>
<li>delete_files()</li>
<li>read_file()</li>
<li>write_file()</li>
<li>make_path()</li>
<li>download()</li>
</ul>
<p>Download <a href="http://www.keyframesandcode.com/resources/php/kohana/helpers/filesystem.zip">filesystem.php</a> here.</p>
<p><span id="more-198"></span></p>
<h3 style="margin-top: 30px !important;">map<span style="font-weight:normal"> ($path, $levels = NULL, $structured = FALSE, $files_first = FALSE)</span></h3>
<p><strong>Returns a hierarchical array of folders and files from the specified path.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : <em>string</em> - path to the folder</li>
<li>[<strong>levels</strong>] : <em>number</em> - limit how many folders deep you want to recursively scan</li>
<li>[<strong>structured</strong>] : <em>boolean</em> - split each returned branch into <em>&#8216;/folder&#8217;</em> and <em>&#8216;/files&#8217;</em> sub-arrays</li>
<li>[<strong>files_first</strong>] : <em>boolean</em> - if not returning as a structured array, list files before folders</li>
</ul>
<h3 style="margin-top: 30px !important;">get_folders<span style="font-weight:normal"> ($path, $appendPath = false)</span></h3>
<p><strong>Returns an array of folders from the specified </strong><strong>path</strong><strong>.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : <em>string</em> - path to the folder</li>
<li>[<strong>append_path</strong>] : <em>boolean</em> - append the original path to the return output</li>
</ul>
<h3 style="margin-top: 30px !important;">get_files<span style="font-weight:normal"> ($path, $mask = NULL, $appendPath = false)</span></h3>
<p><strong>Returns an array of files from the specified </strong><strong>path</strong><strong>.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : <em>string</em> - path to the folder</li>
<li>[<strong>mask</strong>] : <em>mixed</em> - a file mask to determine which files to return. Can be either
<ul>
<li><em>null</em> - all files</li>
<li><em>string</em> - a single file extension</li>
<li><em>array</em> - an array of file extensions</li>
<li><em>regular expression</em> - any valid regular expression, for example &#8216;/^config/&#8217;</li>
</ul>
</li>
<li>[<strong>append_path</strong>] : <em>boolean</em> - append the original path to the return output</li>
</ul>
<h3 style="margin-top: 30px !important;">delete_files<span style="font-weight:normal"> ($path, $mask = NULL, $del_dir = FALSE, $del_root = FALSE)</span></h3>
<p><strong>Recursively deletes all files and optionally folders from the specified </strong><strong>path</strong><strong>.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : <em>string</em> - path to the folder</li>
<li>[<strong>mask</strong>] : <em>mixed</em> - a file mask to determine which files to delete. Can be either
<ul>
<li><em>null</em> - all files</li>
<li><em>string</em> - a single file extension</li>
<li><em>array</em> - an array of file extensions</li>
<li><em>regular expression</em> - any valid regular expression, for example &#8216;/^config/&#8217;</li>
</ul>
</li>
<li>[<strong>del_dir</strong>] : <em>boolean</em> - delete any directories encountered when prcoessing</li>
<li>[<strong>del_root</strong>] : <em>boolean</em> - delete the root directory after processing</li>
</ul>
<h3 style="margin-top: 30px !important;">read_file<span style="font-weight:normal"> ($file)</span></h3>
<p><strong>Opens the specified </strong><strong>file </strong><strong>and returns its contents as a string.</strong></p>
<p>The parameters are:</p>
<ul>
<li><strong>file</strong>: <em>string</em> - path to the folder</li>
</ul>
<h3 style="margin-top: 30px !important;">write_file<span style="font-weight:normal"> ($path, $data, $append = FALSE)</span></h3>
<p><strong>Writes data to the specified </strong><strong>file</strong><strong>.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : <em>string</em> - path to the folder</li>
<li><strong>data</strong> : <em>string</em> - the data you want written to the file</li>
<li>[<strong>append</strong>] - <em>boolean</em> - optionally append data to the file, rather than overwriting it</li>
</ul>
<h3 style="margin-top: 30px !important;">make_path<span style="font-weight:normal"> ($path, $permissions = 0755)</span></h3>
<p><strong>Recursively create folders to form the specified path.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>path</strong> : mixed - path or an array of paths</li>
<li>[<strong>permissions</strong>]: <em>number</em> - permissions you want granted to the new folders</li>
</ul>
<h3 style="margin-top: 30px !important;">download<span style="font-weight:normal"> ($filedata, $filename = NULL)</span></h3>
<p><strong>Download file content by popping up a download dialog in the user&#8217;s browser.</strong></p>
<p>The parameters are</p>
<ul>
<li><strong>filedata</strong> : <em>mixed</em> - can either be
<ul>
<li><em>filepath</em> - the path to an existing file</li>
<li><em>string</em> - data you want downloaded as a file</li>
</ul>
</li>
<li>[<strong>filename</strong>] : <em>string</em> -  a filename that will appear in the user&#8217;s download dialog</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/kohana-filesystem-helper/feed/</wfw:commentRss>
		</item>
		<item>
		<title>I love CodeIgniter!</title>
		<link>http://www.keyframesandcode.com/code/development/i-love-codeigniter/</link>
		<comments>http://www.keyframesandcode.com/code/development/i-love-codeigniter/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 16:27:44 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[CodeIgniter]]></category>

		<category><![CDATA[Development]]></category>

		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[menus]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/?p=193</guid>
		<description><![CDATA[I don&#8217;t normally write a post unless I&#8217;m specifically publishing code, but I wanted to write a brief post about Code Igniter as it&#8217;s the best damn thing I&#8217;ve discovered since jQuery!
Overview
If you haven&#8217;t heard of it already, CodeIgniter is a rapid application-development framework for PHP that uses the Model-View-Controller pattern to separate the presentation, [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t normally write a post unless I&#8217;m specifically publishing code, but I wanted to write a brief post about Code Igniter as it&#8217;s the best damn thing I&#8217;ve discovered since jQuery!</p>
<h3>Overview</h3>
<p>If you haven&#8217;t heard of it already, <a href="http://codeigniter.com/">CodeIgniter</a> is a rapid application-development <a href="http://en.wikipedia.org/wiki/Software_framework">framework</a> for PHP that uses the <a href="http://en.wikipedia.org/wiki/Model-view-controller">Model-View-Controller</a> pattern to separate the presentation, logic and data from each other whilst building your web application.</p>
<p>What this means in practice is that instead of building one large linear PHP file that&#8230;</p>
<ol>
<li>Contains swathes of PHP in the head to filter user input (logic), then</li>
<li>Gets results from a database (data) and does something meaningful with them, then finally</li>
<li>Outputs the HTML (presentation) and continues to escape in and out of PHP during the process</li>
</ol>
<p>&#8230;you end up working on many different smaller files that do one task only, with which the framework pieces together for you to deliver finished web pages.</p>
<p>However, before I delve into MVC, I want to touch upon CodeIgniters excellent class library.</p>
<h3>A excellent and fully-integrated set of Library classes</h3>
<p>The primary reason I started using CodeIgniter in the first place, is that it provides an incredibly robust and integrated set of classes to handle everything web-app related, from database classes, to session classes, to file uploading and email.</p>
<p>Had it not been for the fact that you have to use them within the overall CodeIgniter structure, I may well have gone off and carried on the way I was going, with logic at the top of my pages, and presentation at the bottom.</p>
<p>However, in order to actually use them, you have to accept you&#8217;ll start coding within the CodeIgniter framework, and things will never be the same again&#8230;</p>
<h3>An intelligent and modular approach to building web applications</h3>
<p>The vast majority of what you do within CodeIgniter will be written within class files, that will fall under one of the three MVC areas:</p>
<ol>
<li>Controllers - classes responsible for making overall decisions, such as deciding what task is being undertaken in response to user-input, as well as loading supporting classes and logic to get the job done. Controllers are the first step in processing a task, and outputting a web page.
<p>It helps to think of controllers and their methods as the &#8220;folder&#8221; and &#8220;page&#8221; structure of a site. For example a &#8220;Home Controller&#8221; might have an &#8220;index&#8221; function would handle loading the home page, whilst a &#8220;Messaging Controller&#8221; and might have a &#8220;List&#8221; and &#8220;Compose&#8221; methods would handle listing messages, composing new messages, etc.</p>
<p>Both these methods would take high-level decisions about the task in hand, offloading the drudgery of the tasks to other elements, such as&#8230;</li>
<li>Models - classes responsible for everything &#8220;data&#8221; related, such as read and writing from databases, storing representations of said data, configuration, etc.
<p>In practice, what this means is that a Controller would never read or write data directly, it would hand all responsibility for that to a Model, and would just make calls such as &#8220;get the last 10 unread messages&#8221;. The model would do the grunt-work, leaving the Controller to get on with more important things.</li>
<li>Views - static or dynamic files that are read by CodeIgniter, populated with data, and are output to the user. Views are analogous with the final page output you&#8217;d see in procedural PHP programming.
<p>A view file need not be an entire web page, it could just be a segment, such as a sidebar, a form, a graph, a header, or a footer. By combining views, you have a lot of power with regards to the output of the final page, as well as a really flexible way to store individual representations of data.</p>
<p>For example, depending on user input, you could load either a Table View or a Graph View that would be populated by the Model data. Inside the view you&#8217;d have written code that would receive Model data and would know what to do with it.</li>
</ol>
<p>The benefit of this approach is that it&#8217;s much easier and quicker to build and maintain your site, as everything&#8217;s modular, and can be loaded and swapped out according to the task at hand (and no more huge sprawling pages).</p>
<p>There aren&#8217;t really that many downsides to this approach, but it can be a little tricky sometimes, as due to the way CodeIgniter works behind teh scenes, you don&#8217;t work with a traditional one-to-one file / page structure, but once you&#8217;re used to it, you&#8217;ll never want to go back.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/i-love-codeigniter/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Comment Headings (beta)</title>
		<link>http://www.keyframesandcode.com/code/development/comment-headings-beta/</link>
		<comments>http://www.keyframesandcode.com/code/development/comment-headings-beta/#comments</comments>
		<pubDate>Sat, 07 Jun 2008 12:26:47 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[ActionScript]]></category>

		<category><![CDATA[Development]]></category>

		<category><![CDATA[Flash]]></category>

		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/development/comment-headings-beta/</guid>
		<description><![CDATA[When navigating through large classes, it can be a real pain to find certain bits of code if you don&#8217;t have a good IDE. As I often work in Flash IDE and DreamWeaver IDE, I have to rely on comment blocks here and there, with lots of &#8212;&#8212;&#8212;- delimiters, etc. which is an OK start.
However, [...]]]></description>
			<content:encoded><![CDATA[<p>When navigating through large classes, it can be a real pain to find certain bits of code if you don&#8217;t have a good IDE. As I often work in Flash IDE and DreamWeaver IDE, I have to rely on comment blocks here and there, with lots of &#8212;&#8212;&#8212;- delimiters, etc. which is an OK start.</p>
<p>However, I saw the PaperVision 3D classes had moved on a step from this, and had rather nice blocks at the top of each script with their logo, made out of characters. Why not extend this idea and actually build a small Flash application to let me build dirty-great big words that I can use in my scripts for navigation purposes?</p>
<h3>Comment Headings beta</h3>
<p>Edit the text in the left-hand text box to update the text in the right, then copy and paste to your IDE.</p>
<p>You&#8217;ll need to be using a monospaced font to make it work! Mac users, you might have to experiment to find the best one&#8230;</p>
<p><strong>UPDATE: the characters below do not appear to display properly in Firefox 3. Investigating&#8230;</strong></p>
<blockquote><p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="600" height="600" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="bgcolor" value="#cccccc" /><param name="src" value="http://www.keyframesandcode.com/resources/flash/applications/Comment%20Headings/CommentHeadings.swf" /><embed type="application/x-shockwave-flash" width="600" height="600" src="http://www.keyframesandcode.com/resources/flash/applications/Comment%20Headings/CommentHeadings.swf" bgcolor="#cccccc"></embed></object></p></blockquote>
<h3>Final release</h3>
<p>The final release, which I&#8217;m working on in my spare time, will have:</p>
<ul>
<li>a greater range of typefaces to choose from</li>
<li>options for other languages (ie, MaxScripters will be able to choose &#8212; for their comment delimiter)</li>
<li>more options with regards to the content and formatting of the comments</li>
<li>options to remember your last headings</li>
<li>Will be built on the MVC design pattern</li>
</ul>
<p>Feel free to make suggestions below.</p>
<h3>Download</h3>
<ul>
<li><a href="http://www.keyframesandcode.com/resources/flash/applications/Comment%20Headings/CommentHeadings(beta).exe">Windows .exe version</a> for Windows users</li>
<li><a href="http://www.keyframesandcode.com/resources/flash/applications/Comment%20Headings/CommentHeadings.zip">Flash .swf version</a> for Mac users</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/comment-headings-beta/feed/</wfw:commentRss>
		</item>
		<item>
		<title>&#8220;List&#8221; struct</title>
		<link>http://www.keyframesandcode.com/code/development/maxscript/list-struct/</link>
		<comments>http://www.keyframesandcode.com/code/development/maxscript/list-struct/#comments</comments>
		<pubDate>Fri, 06 Jun 2008 18:21:21 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[For Scripters]]></category>

		<category><![CDATA[Functions]]></category>

		<category><![CDATA[MaxScript]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/development/maxscript/for-scripters/list-struct/</guid>
		<description><![CDATA[As 3dsmax doesn&#8217;t allow for either associative arrays or dynamically-set object properties, it can be difficult to store unstructured, arbitrary variables.
Sometimes you just want to store lists of name/value pairs, to keep track of a few settings throughout your script, without relying on a host of variables, resorting to .ini files, or custom attributes.
Therefore, I [...]]]></description>
			<content:encoded><![CDATA[<p>As 3dsmax doesn&#8217;t allow for either associative arrays or dynamically-set object properties, it can be difficult to store unstructured, arbitrary variables.</p>
<p>Sometimes you just want to store lists of name/value pairs, to keep track of a few settings throughout your script, without relying on a host of variables, resorting to .ini files, or custom attributes.</p>
<p>Therefore, I set about writing a basic Dictionary, or List struct, similar to VB and other languages.</p>
<h3>Example code</h3>
<p>Here&#8217;s a really basic example of storing 5 named variables within a List:</p>
<pre>names = #("five", "four", "three", "two", "one")
values = #(5,4,3,2,1)
lst = List()

for i = 1 to 5 do lst.addItem names[i] values[i]</pre>
<p>So let&#8217;s get some data back out:</p>
<pre>lst.getValue "two"
-- 2

lst.getIndex "two"
-- 4

lst.getValues()
-- #(5, 4, 3, 2, 1)</pre>
<p>Or print the whole lot:</p>
<pre>lst.print() -- unsorted

five:	5
four:	4
three:	3
two:	2
one:	1</pre>
<p>How about some sorting:</p>
<pre>lst.sort() -- by name, alphabetically

five:	5
four:	4
one:	1
three:	3
two:	2

lst.sort field:#value -- by value

one:	1
two:	2
three:	3
four:	4
five:	5</pre>
<p>As you can see, it&#8217;s pretty straightforward stuff!</p>
<h3>Properties and Methods</h3>
<blockquote>
<h3>Properties</h3>
<ul>
<li>&lt;array&gt; <strong>items</strong> &lt;name&gt; &lt;value&gt; - the key/value pairs.
<ul>
<li>Names can be a #name, &#8220;string&#8221;, or even an index</li>
<li>Values can be any MaxWrapper value (i.e. anything)</li>
</ul>
</li>
</ul>
<h3>Setters</h3>
<ul>
<li>&lt;ListItem&gt; <strong>addItem </strong>&lt;name&gt; &lt;value&gt; - adds an item to the List, and if it already exists</li>
<li>&lt;ListItem&gt; <strong>setItem </strong>&lt;name&gt; &lt;value&gt; - synonym for the above</li>
</ul>
<h3>Getters</h3>
<ul>
<li>&lt;value&gt; <strong>getValue </strong>&lt;name&gt; - returns the value of the named item</li>
<li>&lt;index&gt; <strong>getIndex </strong>&lt;name&gt; - returns the index of the named item</li>
<li>&lt;name&gt; <strong>getName </strong>&lt;value&gt; - returns the name of the first item that matches the supplied value</li>
<li>&lt;ListItem&gt; <strong>getItem </strong>&lt;name&gt; - returns the List item corresponding to the supplied name (typically, you wouldn&#8217;t use this, as you know the name component already, it&#8217;s just included for completeness)</li>
<li>&lt;array&gt; <strong>getItems()</strong> - returns all items as an array of ListItem structs</li>
<li>&lt;array&gt; <strong>getNames()</strong> - returns all names as an array</li>
<li>&lt;array&gt; <strong>getValues() </strong>- returns all values as an array</li>
</ul>
<h3>Clear or delete</h3>
<ul>
<li>&lt;array&gt; <strong>clear()</strong> - clears the lit of all items, and returns the empty ListItems array</li>
<li>&lt;boolean&gt; <strong>deleteItem </strong>&lt;name&gt; - deletes the named item from the list, and returns it</li>
<li>&lt;boolean&gt; <strong>deleteIndex </strong>&lt;index&gt; - deletes the item at the index, and returns it</li>
</ul>
<h3>Utilities</h3>
<ul>
<li>&lt;array&gt; <strong>sort</strong> field:&lt;name&gt; order:&lt;name&gt; func:&lt;function&gt; - sorts the list in a variety of ways, even supply a comparison function (see the max help on qsort)</li>
<li>&lt;string&gt; <strong>print()</strong> - prints the List items to the Listener</li>
</ul>
</blockquote>
<h3>Next version</h3>
<p>Possible improvements in the next version might be:</p>
<ul>
<li>.ini file integration, with <strong>save()</strong> and <strong>load()</strong> methods</li>
<li>Support for hierarchical Lists, perhaps <strong>getValue #(#parent, #child, #grandchild, &#8230;)</strong></li>
</ul>
<h3>Download</h3>
<p>Download <a href="http://www.keyframesandcode.com/resources/maxscript/Functions/List.struct.ms">List.struct.ms</a> here.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/maxscript/list-struct/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Time Stamper - Meshops.attach() Case Study</title>
		<link>http://www.keyframesandcode.com/code/development/maxscript/time-stamper-meshopsattach-case-study/</link>
		<comments>http://www.keyframesandcode.com/code/development/maxscript/time-stamper-meshopsattach-case-study/#comments</comments>
		<pubDate>Mon, 26 May 2008 00:00:00 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[MaxScript]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/development/maxscript/time-stamper-meshopsattach-case-study/</guid>
		<description><![CDATA[Overview
In response to a post on CG Talk,  Optimization ideas needed, a few other developers and I us pitched in to help out with ideas to solve a puzzling problem.
The issue was that a loop to attach 7500 objects was taking up to 8 minutes to complete. Where did the problem lie? Was it [...]]]></description>
			<content:encoded><![CDATA[<h3>Overview</h3>
<p>In response to a post on CG Talk,  <a href="http://forums.cgsociety.org/showthread.php?p=5172805#post5172805">Optimization ideas needed</a>, a few other developers and I us pitched in to help out with ideas to solve a puzzling problem.</p>
<p>The issue was that a loop to attach 7500 objects was taking up to 8 minutes to complete. Where did the problem lie? Was it in the transformation code, the attachment code, or somewhere else?</p>
<p>Well as luck would have it, today&#8217;s Bank Holiday has been totally washed out with rain, so I had nothing better to than to do some testing and find the cause, and in the process road test my recently-developed <a href="http://www.keyframesandcode.com/code/development/maxscript/time-stamper/">TimeStamper</a> struct.</p>
<h3>Approach</h3>
<p>After turning undo and redraw off, as well as optimizing the transformation code within the script, which cut a 1000-object test scene&#8217;s processing time in half, surprisingly the 7500-object scene was still taking around 8 minutes.</p>
<p>I suspected that the problem may well be in the fact that as mesh gets larger, perhaps it becomes computationally more expensive to add new meshes, seeing as the entire array of points and faces needs to be evaluated every time a new mesh is added. Cumulatively, this could really add up.</p>
<p>I though that perhaps the right approach would be to split the attaching up into phases, using a 2D loop, and I suspected that perhaps the square-root of the total attaches might yield the optimal performance, ie for 10000 attaches I&#8217;d perform a 100 x 100 loop, creating 100 intermediate meshes, then attaching them all at the end.</p>
<h3> Results</h3>
<p>These are the results for various variations of 2D loops, limiting the amount of objects attached per loop. The results are impressive!</p>
<ul>
<li>For 1000 objects, the optimum loop combination was 31 loops of 32, a speed increase of <strong>12 </strong>times over a single loop</li>
<li>For 3000 objects, the optimum loop combination was 55 loops of 54, a speed increase of <strong>22 </strong>times over a single loop</li>
<li>For 5000 objects, the optimum loop combination was 50 loops of 100, a speed increase of <strong>28 </strong>times over a single loop</li>
<li>For 7500 objects, the optimum loop combination was 87 loops of 86, a speed increase of <strong>30 </strong>times over a single loop</li>
</ul>
<p>And yes, the square root approach was most successful 3 out of 4 times.</p>
<p>Here are the results in table format, with the optimum loop highlighted:</p>
<p><img src="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/img/time-stamper-table.gif" /></p>
<h3>1D / 2D loop comparison</h3>
<p>Compared to the 1D loop, the 2D loop was <strong>30</strong> times faster on <strong>7500 </strong>attachments.</p>
<p>In minutes and seconds, that means a decrease from <strong>6 minutes and 29 seconds</strong> to just <strong>12 seconds</strong>!</p>
<p>In graph-form that looks like this:</p>
<p><img src="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/img/time-stamper-graph.gif" /></p>
<h3>Conclusion</h3>
<p>It seems pretty safe to say that in this case, building up intermediate meshes and attaching at the end is the best way to go. It looks likely that adding to already-huge arrays carries with it an overhead that can have an extremely negative effect in a lengthy loop situation.</p>
<p>I&#8217;ve no doubt that this approach most likely extends to other mesh / array / hash based computations as well, so splitting up large cumulatively-expensive calculations into smaller chunks could well be a great time-saver in other tasks as well.</p>
<p>So - a shame about the rain, but perhaps a bonus in disguise!</p>
<h3>Resources</h3>
<ul>
<li>View the <a href="http://www.keyframesandcode.com/code/development/maxscript/time-stamper/">Time Stamper page</a></li>
<li>Download <a href="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/TimeStamper.ms">TimeStamper.ms</a></li>
<li>Download the <a href="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/TimeStamper-create-spheres.ms">test script</a> that generates the 1000&#8217;s of spheres that get attached, and performs the benchmarking</li>
<li>Download the Excel report that was built using TimeStamper getReport() function in <a href="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/docs/TimeStamper-report.xls">Excel 2003</a> or <a href="http://www.keyframesandcode.com/resources/maxscript/For%20Scripters/Time%20Stamper/docs/TimeStamper-report.xlsx">Excel 2007</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/maxscript/time-stamper-meshopsattach-case-study/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Transform Presets</title>
		<link>http://www.keyframesandcode.com/code/development/maxscript/transform-presets/</link>
		<comments>http://www.keyframesandcode.com/code/development/maxscript/transform-presets/#comments</comments>
		<pubDate>Sat, 17 May 2008 00:00:41 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Animation]]></category>

		<category><![CDATA[Cameras]]></category>

		<category><![CDATA[MaxScript]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/development/maxscript/transform-presets/</guid>
		<description><![CDATA[Transform Presets is a pretty simple plugin - it allows you to store multiple transformations of an object (and it&#8217;s target, if there is one) in a list, to call up when you like.
I built it for fun in response to a thread on CGTalk.
Screenshots
Here it is working on a target camera. Note both the [...]]]></description>
			<content:encoded><![CDATA[<p>Transform Presets is a pretty simple plugin - it allows you to store multiple transformations of an object (and it&#8217;s target, if there is one) in a list, to call up when you like.</p>
<p>I built it for fun in response to <a href="http://forums.cgsociety.org/showthread.php?f=98&amp;t=632251">a thread on CGTalk</a>.</p>
<h3>Screenshots</h3>
<p>Here it is working on a target camera. Note both the camera and target are transformed.</p>
<p><img src="http://www.keyframesandcode.com/resources/maxscript//Animation/Transform%20Presets/transform-presets-camera.gif" /></p>
<p>And here it is working with an IK handle. Note the swivel angle is updated as well as the transform.</p>
<p><img src="http://www.keyframesandcode.com/resources/maxscript//Animation/Transform%20Presets/transform-presets-ik.gif" /></p>
<h3>Download and installation</h3>
<p>Download <a href="http://www.keyframesandcode.com/resources/maxscript/Animation/Transform%20Presets/TransformPresets.mzp">TransformPresets.mzp</a> and then drag the .mzp file onto one of your 3dsmax viewports, which will install the script in the correct directory. Then you can add the script to a toolbar / quad by going to:</p>
<ul>
<li>Customize &gt; Customize User Interface…</li>
<li>Then look under the <strong>Animation Tools</strong> category for <strong>Transform Presets</strong></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/development/maxscript/transform-presets/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Towards an optimal method of quoting - Dom&#8217;s response</title>
		<link>http://www.keyframesandcode.com/code/freelance/doms-response/</link>
		<comments>http://www.keyframesandcode.com/code/freelance/doms-response/#comments</comments>
		<pubDate>Thu, 15 May 2008 13:14:06 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Freelance]]></category>

		<category><![CDATA[Quoting]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/freelance/doms-response/</guid>
		<description><![CDATA[Hi DJ
Well done to you for tackling  this one, is the hardest part of this design lark for sure
After reading everyones comments here I am glad I choose to avoid print and web design. Animation is easier as you dont have to quantify exact details to your client, you dont usually have to work [...]]]></description>
			<content:encoded><![CDATA[<p>Hi DJ</p>
<p>Well done to you for tackling  this one, is the hardest part of this design lark for sure</p>
<p>After reading everyones comments here I am glad I choose to avoid print and web design. Animation is easier as you dont have to quantify exact details to your client, you dont usually have to work with complicated file structures as you would with someone’s company branding/website.</p>
<p>To be quick, I just spend as much time as possible in a one to one basis trying to uncover the clients aspirations for the product, I then give them some reality of costs using other projects they will know as case studies. After this they usually understand cost vs &#8216;quality&#8217;. If they understand these factors its easy to manage the project from then in.</p>
<p>I think having T&amp;C’s are important in quotes but mainly to protect you from a muppet client. If you have &#8216;managed&#8217; the client well you shouldn&#8217;t need T&amp;C’s too much.</p>
<p>I am trying to build a business that services clients&#8217; long term needs, that way I avoid pissing matches with details/arguments as we build a relationship of trust with clients who then get out of the loop THUS saving eveyone money.. i hope</p>
<p>Dom</p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/freelance/doms-response/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Towards an optimal method of quoting - Matt&#8217;s response</title>
		<link>http://www.keyframesandcode.com/code/freelance/matts-response/</link>
		<comments>http://www.keyframesandcode.com/code/freelance/matts-response/#comments</comments>
		<pubDate>Thu, 15 May 2008 11:59:17 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Freelance]]></category>

		<category><![CDATA[Quoting]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/freelance/matts-response/</guid>
		<description><![CDATA[Hi Dave.
Loads of thoughts on this subject.
Will write them up for you when I have a sec.
Good instigation.
In the meantime, we generally find that no matter how hard we try http://en.wikipedia.org/wiki/Hofstadter&#8217;s_Law is always right&#8230;
Cheers.
M
]]></description>
			<content:encoded><![CDATA[<p>Hi Dave.</p>
<p>Loads of thoughts on this subject.</p>
<p>Will write them up for you when I have a sec.</p>
<p>Good instigation.</p>
<p>In the meantime, we generally find that no matter how hard we try <a href="http://en.wikipedia.org/wiki/Hofstadter's_Law">http://en.wikipedia.org/wiki/Hofstadter&#8217;s_Law</a> is always right&#8230;</p>
<p>Cheers.</p>
<p>M</p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/freelance/matts-response/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Towards an optimal method of quoting - Zac&#8217;s response</title>
		<link>http://www.keyframesandcode.com/code/freelance/zacs-response/</link>
		<comments>http://www.keyframesandcode.com/code/freelance/zacs-response/#comments</comments>
		<pubDate>Thu, 15 May 2008 10:48:43 +0000</pubDate>
		<dc:creator>Dave Stewart</dc:creator>
		
		<category><![CDATA[Freelance]]></category>

		<category><![CDATA[Quoting]]></category>

		<guid isPermaLink="false">http://www.keyframesandcode.com/code/uncategorized/zacs-response/</guid>
		<description><![CDATA[Hi Dave,
Ok i&#8217;m afraid i&#8217;ve gone down the road of quoting for the perfect job.
Recently I quoted for a charity rebrand where I estimated the time for the logo to be x amount - this was 4 concepts and 1 concept to be carried across into the final design. All was well until they kept [...]]]></description>
			<content:encoded><![CDATA[<p class="MsoNormal"><span>Hi Dave,<o:p></o:p></span></p>
<p class="MsoNormal"><span>Ok i&#8217;m afraid i&#8217;ve gone down the road of quoting for the perfect job.</span></p>
<p class="MsoNormal"><span>Recently I quoted for a charity rebrand where I estimated the time for the logo to be x amount - this was 4 concepts and 1 concept to be carried across into the final design. All was well until they kept changing their mind (as clients do). When they were satisified with the logo they said &#8220;now we just need to show it to the board&#8217;!!</span></p>
<p class="MsoNormal"><span>As you can tell the amount of extra time I have had to put into the project is ridiculous. Once they showed the board they chose one of the designs and they got back to me the next day saying the design was too similar to another charity logo!</span></p>
<p class="MsoNormal"><span>So i&#8217;m still re-working the logo and the client is getting more and more frustrated. But i&#8217;ve stated that anything that goes over the given timescale is chargeable x per hour. If they had told me upfront that the design had to go through the board then I would have factored more time in&#8230;.it&#8217;s probably my fault for not asking!</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span>Here is a list of things that I have learnt from larger design projects:<o:p></o:p></span></p>
<ul>
<li><span>Create a document with given timescales and an initial fixed value. Break the project up into smaller chunks. i.e. Concept Stage, Implementation and Programming<o:p></o:p></span></li>
<li><span>Charge the client 50% upfront and 50% on completion of the project OR into 3 stages and everything has to be signed off and invoiced at each stage<o:p></o:p></span></li>
<li><span>Create a T&amp;Cs document for both parties to sign this includes cancellation and termination of project.<o:p></o:p></span></li>
<li><span>Regularly update the client with how much extra work you&#8217;re carrying out and cost so they don&#8217;t get a shock at the end of the project<o:p></o:p></span></li>
</ul>
<p class="MsoNormal"><span><o:p></o:p>I&#8217;ve also said to clients that within the quote x amount of days for changes. The &#8217;rounds of changes&#8217; has never really worked for me, my clients find this too restricting.</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span><o:p>I </o:p>have a client who is spot on with organising everything from the start. Word documents, images in alphabetical order etc etc. so I quote her less because the project is quicker to finish. With some clients you just know that the project is going to take longer because they are wishy washy, they faff about and they keep changing their mind. <o:p></o:p></span></p>
<p><span>The worst one for me is text changes and typos. I get so frustrated with really small changes which end up taking forever. It&#8217;s really difficult to know exactly how much time to factor within the quote for these kind of things because these small changes end up being time consuming.<o:p></o:p></span></p>
<p class="MsoNormal"><span><o:p></o:p>Unforseen - extra meetings. Sometimes I put in the quote &#8216;meetings not included&#8217; within the quote so they are charged separately, I add travel on to this as well.<o:p></o:p></span></p>
<p class="MsoNormal"><span>I think that&#8217;s all for now,<o:p></o:p></span></p>
<p class="MsoNormal"><span>Zac<o:p></o:p></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.keyframesandcode.com/code/freelance/zacs-response/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
