<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	
	>
<channel>
	<title>Comments on: Thoughts on semantics for 3D graphics</title>
	<atom:link href="http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/feed" rel="self" type="application/rss+xml" />
	<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics</link>
	<description>Inspirations &#38; experiments, mainly about denotative/functional programming in Haskell</description>
	<lastBuildDate>Sat, 26 Sep 2020 21:06:12 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.17</generator>
	<item>
		<title>By: Conal</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-128816</link>
		<dc:creator><![CDATA[Conal]]></dc:creator>
		<pubDate>Fri, 01 May 2020 02:19:56 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-128816</guid>
		<description><![CDATA[&lt;p&gt;Terrific. Thanks again, Doug! I&#039;ve missed working in graphics, and I can use some inspiration right now.&lt;/p&gt;

&lt;p&gt;I bet a lovely repackaging of your work would be as a reinterpretation of Haskell programs via &lt;a href=&quot;http://conal.net/papers/compiling-to-categories&quot; rel=&quot;nofollow&quot;&gt;&lt;em&gt;Compiling to categories&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>Terrific. Thanks again, Doug! I&#8217;ve missed working in graphics, and I can use some inspiration right now.</p>

<p>I bet a lovely repackaging of your work would be as a reinterpretation of Haskell programs via <a href="http://conal.net/papers/compiling-to-categories" rel="nofollow"><em>Compiling to categories</em></a>.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Doug Moen</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-128815</link>
		<dc:creator><![CDATA[Doug Moen]]></dc:creator>
		<pubDate>Fri, 01 May 2020 02:06:17 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-128815</guid>
		<description><![CDATA[&lt;p&gt;Hi Conal. My project is https://github.com/curv3d/curv. Curv is a curried, pure functional language that I created as a DSL for making 2D and 3D shapes. You&#039;ve done a lot of research that matches up with what I&#039;m trying to do, so I&#039;m reading your papers now: FRP, Tangible Values, etc. (My shapes are already continuous functions of time; next step is to make them more generally interactive, beyond what I currently do using sliders to change parameters.)&lt;/p&gt;

&lt;p&gt;An excellent reference is Sphere Tracing by John C. Hart: https://github.com/curv3d/curv/blob/master/docs/papers/sphere_tracing.pdf&lt;/p&gt;

&lt;p&gt;Here&#039;s a paper I wrote on the math behind Curv&#039;s shape representation. It says some things that Hart does not, which are relevant to Curv: https://github.com/curv3d/curv/blob/master/docs/Theory.rst&lt;/p&gt;

&lt;p&gt;My library of shape combinators is documented here: https://github.com/curv3d/curv/tree/master/docs/shapes&lt;/p&gt;

&lt;p&gt;This doc explains that a shape has a distance field, and it classifies distance fields as exact, mitred, approximate, bad and discontinuous. You need to know this because shape combinators are sensitive to the class of distance field they receive as input, and they may return a worse class of distance than what they received as input.
https://github.com/curv3d/curv/blob/master/docs/shapes/Shapes.rst&lt;/p&gt;

&lt;p&gt;The documentation for each shape combinator describes its limitations with respect to the classes of distance field that it accepts as input and produces as output.&lt;/p&gt;

&lt;p&gt;Other people have addressed this problem (of users needing to worry about distance field properties) by restricting expressiveness, limiting the set of shapes and shape combinators you can define or use. All of the related projects listed below restrict expressiveness and thereby make the abstraction less leaky.&lt;/p&gt;

&lt;p&gt;Related projects:&lt;/p&gt;

&lt;p&gt;ImplicitCAD is written in Haskell, and uses the same signed distance representation. No GPU compilation, and there is a fixed set of distance field operations hard coded into the library, which you have to hack to define new primitives. It&#039;s not extensible like Curv is. No colour or animation. https://github.com/colah/ImplicitCAD&lt;/p&gt;

&lt;p&gt;libfive uses Scheme as its extension language. It has some restrictions relative to Curv: distance functions must be closed form expressions (no conditionals, no recursion), so many Curv primitives cannot be ported. But, there is no Lipshitz continuity restriction on distance functions, so this restriction removes a burden from users. Shapes are converted to a triangle mesh before being displayed, so no fine detail, no infinite shapes. Also no colour or animation. The author, Matt Keeter, has a paper in this year&#039;s SigGraph on compiling his closed-form distance functions to GPU shader code without using sphere tracing. This is a new algorithm: the paper and the source code are not yet available at time of writing.&lt;/p&gt;

&lt;p&gt;The video game &quot;Dreams&quot; on Playstation by Media Molecule uses signed distance fields to represent all geometric shapes in the game. You can create your own content using an editor, by unioning, differencing and blending a small fixed set of geometric primitives. The set of geometric primitives and shape operators is sharply limited to make everything well behaved. The GPU implementation is quite interesting and impressive; I&#039;m studying it with an eye to duplicating their best tricks in Curv some day. https://www.mediamolecule.com/blog/article/siggraph_2015&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>Hi Conal. My project is <a href="https://github.com/curv3d/curv" rel="nofollow">https://github.com/curv3d/curv</a>. Curv is a curried, pure functional language that I created as a DSL for making 2D and 3D shapes. You&#8217;ve done a lot of research that matches up with what I&#8217;m trying to do, so I&#8217;m reading your papers now: FRP, Tangible Values, etc. (My shapes are already continuous functions of time; next step is to make them more generally interactive, beyond what I currently do using sliders to change parameters.)</p>

<p>An excellent reference is Sphere Tracing by John C. Hart: <a href="https://github.com/curv3d/curv/blob/master/docs/papers/sphere_tracing.pdf" rel="nofollow">https://github.com/curv3d/curv/blob/master/docs/papers/sphere_tracing.pdf</a></p>

<p>Here&#8217;s a paper I wrote on the math behind Curv&#8217;s shape representation. It says some things that Hart does not, which are relevant to Curv: <a href="https://github.com/curv3d/curv/blob/master/docs/Theory.rst" rel="nofollow">https://github.com/curv3d/curv/blob/master/docs/Theory.rst</a></p>

<p>My library of shape combinators is documented here: <a href="https://github.com/curv3d/curv/tree/master/docs/shapes" rel="nofollow">https://github.com/curv3d/curv/tree/master/docs/shapes</a></p>

<p>This doc explains that a shape has a distance field, and it classifies distance fields as exact, mitred, approximate, bad and discontinuous. You need to know this because shape combinators are sensitive to the class of distance field they receive as input, and they may return a worse class of distance than what they received as input.
<a href="https://github.com/curv3d/curv/blob/master/docs/shapes/Shapes.rst" rel="nofollow">https://github.com/curv3d/curv/blob/master/docs/shapes/Shapes.rst</a></p>

<p>The documentation for each shape combinator describes its limitations with respect to the classes of distance field that it accepts as input and produces as output.</p>

<p>Other people have addressed this problem (of users needing to worry about distance field properties) by restricting expressiveness, limiting the set of shapes and shape combinators you can define or use. All of the related projects listed below restrict expressiveness and thereby make the abstraction less leaky.</p>

<p>Related projects:</p>

<p>ImplicitCAD is written in Haskell, and uses the same signed distance representation. No GPU compilation, and there is a fixed set of distance field operations hard coded into the library, which you have to hack to define new primitives. It&#8217;s not extensible like Curv is. No colour or animation. <a href="https://github.com/colah/ImplicitCAD" rel="nofollow">https://github.com/colah/ImplicitCAD</a></p>

<p>libfive uses Scheme as its extension language. It has some restrictions relative to Curv: distance functions must be closed form expressions (no conditionals, no recursion), so many Curv primitives cannot be ported. But, there is no Lipshitz continuity restriction on distance functions, so this restriction removes a burden from users. Shapes are converted to a triangle mesh before being displayed, so no fine detail, no infinite shapes. Also no colour or animation. The author, Matt Keeter, has a paper in this year&#8217;s SigGraph on compiling his closed-form distance functions to GPU shader code without using sphere tracing. This is a new algorithm: the paper and the source code are not yet available at time of writing.</p>

<p>The video game &#8220;Dreams&#8221; on Playstation by Media Molecule uses signed distance fields to represent all geometric shapes in the game. You can create your own content using an editor, by unioning, differencing and blending a small fixed set of geometric primitives. The set of geometric primitives and shape operators is sharply limited to make everything well behaved. The GPU implementation is quite interesting and impressive; I&#8217;m studying it with an eye to duplicating their best tricks in Curv some day. <a href="https://www.mediamolecule.com/blog/article/siggraph_2015" rel="nofollow">https://www.mediamolecule.com/blog/article/siggraph_2015</a></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Conal</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-128806</link>
		<dc:creator><![CDATA[Conal]]></dc:creator>
		<pubDate>Fri, 01 May 2020 00:42:19 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-128806</guid>
		<description><![CDATA[&lt;p&gt;Thanks for the comment, Doug! I&#039;d be grateful for more references, including how the Lipschitz condition relates to sphere tracing and how that requirement gets violated. Also the project you&#039;re working on (if public) and/or related projects. Also the project you&#039;re working on (if public) and/or related projects, as well as the work you mentioned by Vladimir Slepnev and by Inigo Quilez.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>Thanks for the comment, Doug! I&#8217;d be grateful for more references, including how the Lipschitz condition relates to sphere tracing and how that requirement gets violated. Also the project you&#8217;re working on (if public) and/or related projects. Also the project you&#8217;re working on (if public) and/or related projects, as well as the work you mentioned by Vladimir Slepnev and by Inigo Quilez.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Doug Moen</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-128777</link>
		<dc:creator><![CDATA[Doug Moen]]></dc:creator>
		<pubDate>Thu, 30 Apr 2020 22:35:50 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-128777</guid>
		<description><![CDATA[&lt;p&gt;I implemented Vladimir Slepnev&#039;s idea (not in Haskell, but another pure functional language). Using signed distance fields as the shape representation gives a continuous functional representation for 3D solids, with union, morph, blending, etc. You can represent a lot of shapes that aren&#039;t representable using traditional methods like vector graphics, or using discrete representations or like voxels or triangle meshes. For example, you can model fractals, which have theoretically infinite detail, which you can zoom into. I compile my functional code into a single GPU shader program, as per Inigo Quilez. I&#039;m pretty excited by the results. But like all representations for 3D solids, it&#039;s a leaky abstraction. You ultimately have to be aware of performance issues, and some shapes can&#039;t be represented because their distance functions would be too expensive to evaluate. Sphere tracing requires the distance function to be Lipschitz(1). If you have a rich set of shape operators, like twist and bend and morph, then the output can violate the Lipschitz(1) requirement in certain cases, and ultimately the user has to be aware of this (leaky abstraction) and work around the problem. I&#039;m extending my program to overcome these problems, so that I can represent a larger set of shapes, by giving the user more control over the internal shape representation, and providing multiple representations and rendering methods, but by making the program more powerful in this way, the leaky abstraction problem gets worse. There is no non-leaky abstraction for real numbers, so it&#039;s not surprising that the same problems reoccur for geometric shapes.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>I implemented Vladimir Slepnev&#8217;s idea (not in Haskell, but another pure functional language). Using signed distance fields as the shape representation gives a continuous functional representation for 3D solids, with union, morph, blending, etc. You can represent a lot of shapes that aren&#8217;t representable using traditional methods like vector graphics, or using discrete representations or like voxels or triangle meshes. For example, you can model fractals, which have theoretically infinite detail, which you can zoom into. I compile my functional code into a single GPU shader program, as per Inigo Quilez. I&#8217;m pretty excited by the results. But like all representations for 3D solids, it&#8217;s a leaky abstraction. You ultimately have to be aware of performance issues, and some shapes can&#8217;t be represented because their distance functions would be too expensive to evaluate. Sphere tracing requires the distance function to be Lipschitz(1). If you have a rich set of shape operators, like twist and bend and morph, then the output can violate the Lipschitz(1) requirement in certain cases, and ultimately the user has to be aware of this (leaky abstraction) and work around the problem. I&#8217;m extending my program to overcome these problems, so that I can represent a larger set of shapes, by giving the user more control over the internal shape representation, and providing multiple representations and rendering methods, but by making the program more powerful in this way, the leaky abstraction problem gets worse. There is no non-leaky abstraction for real numbers, so it&#8217;s not surprising that the same problems reoccur for geometric shapes.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Vladimir Slepnev</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-512</link>
		<dc:creator><![CDATA[Vladimir Slepnev]]></dc:creator>
		<pubDate>Sun, 11 May 2014 10:35:29 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-512</guid>
		<description><![CDATA[&lt;p&gt;This comment is about 5 years late, but I have to write it because I know exactly the right answer to your question! Look up the work of Inigo Quilez on rendering with distance fields.&lt;/p&gt;

&lt;p&gt;The basic idea is to represent a 3D object as a &quot;distance field&quot;, i.e. a continuous function from R^3 to R that returns the distance to the object. The value of that function is zero inside the object, and grows as you get further away from it. That technique is well suited to ray marching on the GPU, because when you&#039;re marching along the ray, evaluating the distance field at the current point gives you the size of the next step, so you can make bigger steps when the ray passes far from the object. Simple objects like spheres have distance fields that are easy to define, and simple operations on objects often correspond to simple operations on distance fields. It&#039;s also easy to approximate things like normal direction, ambient occlusion, and even penumbras (send a ray to the light source and note how closely it passed to the occluder).&lt;/p&gt;

&lt;p&gt;In a perfect world, we could have a combinator library in Haskell for rendering distance fields that would compile to a single GPU shader program. It&#039;s scary how well this idea fits together.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>This comment is about 5 years late, but I have to write it because I know exactly the right answer to your question! Look up the work of Inigo Quilez on rendering with distance fields.</p>

<p>The basic idea is to represent a 3D object as a &#8220;distance field&#8221;, i.e. a continuous function from R^3 to R that returns the distance to the object. The value of that function is zero inside the object, and grows as you get further away from it. That technique is well suited to ray marching on the GPU, because when you&#8217;re marching along the ray, evaluating the distance field at the current point gives you the size of the next step, so you can make bigger steps when the ray passes far from the object. Simple objects like spheres have distance fields that are easy to define, and simple operations on objects often correspond to simple operations on distance fields. It&#8217;s also easy to approximate things like normal direction, ambient occlusion, and even penumbras (send a ray to the light source and note how closely it passed to the occluder).</p>

<p>In a perfect world, we could have a combinator library in Haskell for rendering distance fields that would compile to a single GPU shader program. It&#8217;s scary how well this idea fits together.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: conal</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-511</link>
		<dc:creator><![CDATA[conal]]></dc:creator>
		<pubDate>Thu, 26 Nov 2009 00:15:20 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-511</guid>
		<description><![CDATA[&lt;blockquote&gt;
  &lt;p&gt;So do you have any intuition about what kind of shapes this essence you’re looking for might take?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, I do.  It will be simple, familiar, and general to math/types/FP folks.  Sum/product/function -- that sort of thing.  It will explain known domain-specific operations as special cases of more general/simple operations.  There will be plenty of &lt;a href=&quot;http://conal.net/blog/tag/type-class-morphism/&quot; rel=&quot;nofollow&quot;&gt;TCMs&lt;/a&gt; in the API and not much else.  Like Reactive&#039;s FRP model built simply from product (Future) and function (Behavior).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;As for extrapolation to yet unknown problems, my feeling is that it will practically come for free if the unification is successful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh -- then I guess we&#039;re on the same track.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<blockquote>
  <p>So do you have any intuition about what kind of shapes this essence you’re looking for might take?</p>
</blockquote>

<p>Yes, I do.  It will be simple, familiar, and general to math/types/FP folks.  Sum/product/function &#8212; that sort of thing.  It will explain known domain-specific operations as special cases of more general/simple operations.  There will be plenty of <a href="http://conal.net/blog/tag/type-class-morphism/" rel="nofollow">TCMs</a> in the API and not much else.  Like Reactive&#8217;s FRP model built simply from product (Future) and function (Behavior).</p>

<blockquote>
  <p>As for extrapolation to yet unknown problems, my feeling is that it will practically come for free if the unification is successful.</p>
</blockquote>

<p>Oh &#8212; then I guess we&#8217;re on the same track.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Patai Gergely</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-510</link>
		<dc:creator><![CDATA[Patai Gergely]]></dc:creator>
		<pubDate>Wed, 25 Nov 2009 21:30:00 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-510</guid>
		<description><![CDATA[&lt;blockquote&gt;My faith (stemming personal intuition and experience) is that when I get to the heart of the matter in elegant isolation, I see pragmatic strengths and defects clearly.&lt;/blockquote&gt;

&lt;p&gt;So do you have any intuition about what kind of shapes this essence you’re looking for might take? Are you looking for the ‘SK calculus of 3D geometry’, or a higher-level view?&lt;/p&gt;

&lt;blockquote&gt;The notion of “real problems” is a slippery one. So often our difficulties are in the questions we’re used to asking and the old paradigms we’re attached to.&lt;/blockquote&gt;

&lt;p&gt;Of course it is slippery, since there are several ways to look at the same thing, which I also alluded to. But I was basically trying to say here that the ultimate ‘grand unified theory’ should be able to capture all these facets, so there will be a point where you’ll have to consider these connections. As for extrapolation to yet unknown problems, my feeling is that it will practically come for free if the unification is successful.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<blockquote>My faith (stemming personal intuition and experience) is that when I get to the heart of the matter in elegant isolation, I see pragmatic strengths and defects clearly.</blockquote>

<p>So do you have any intuition about what kind of shapes this essence you’re looking for might take? Are you looking for the ‘SK calculus of 3D geometry’, or a higher-level view?</p>

<blockquote>The notion of “real problems” is a slippery one. So often our difficulties are in the questions we’re used to asking and the old paradigms we’re attached to.</blockquote>

<p>Of course it is slippery, since there are several ways to look at the same thing, which I also alluded to. But I was basically trying to say here that the ultimate ‘grand unified theory’ should be able to capture all these facets, so there will be a point where you’ll have to consider these connections. As for extrapolation to yet unknown problems, my feeling is that it will practically come for free if the unification is successful.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jake McArthur</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-509</link>
		<dc:creator><![CDATA[Jake McArthur]]></dc:creator>
		<pubDate>Wed, 25 Nov 2009 01:27:26 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-509</guid>
		<description><![CDATA[&lt;p&gt;I think maybe I pushed that &quot;leaky abstraction&quot; analogy a bit far. All I really mean is that I prefer a model with explicit flaws over a model with hidden flaws.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>I think maybe I pushed that &#8220;leaky abstraction&#8221; analogy a bit far. All I really mean is that I prefer a model with explicit flaws over a model with hidden flaws.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jake McArthur</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-508</link>
		<dc:creator><![CDATA[Jake McArthur]]></dc:creator>
		<pubDate>Wed, 25 Nov 2009 01:25:29 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-508</guid>
		<description><![CDATA[&lt;p&gt;&quot;Hm. It feels good to come clean. No wonder Catholics like going to confession.&quot;&lt;/p&gt;

&lt;p&gt;Exposing the limitations of a model also gives me a clearer picture of it, which makes it simpler for me to reason about, although it loses a superficial appearance of simplicity in the process. Perhaps a good analogy would be my taste for Arch Linux over Ubuntu. They both share the same basic architecture, but Ubuntu hides its warts with a simple interface, having the advantage of not initially confusing a user, but the disadvantage of masking the true cause of problems that do arise, while Arch intentionally exposes the inherent complexity of its architecture, perhaps making the learning curve steeper, but also making it easier to reason about due to its full disclosure. While Ubuntu has an abstraction which Arch lacks, it is not a good abstraction because it leaks, therefore the user must learn not only the abstraction but also how it works. Masking or failing to make clear the inherent flaws of a model, I think, is kind of like a leaky abstraction.&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>&#8220;Hm. It feels good to come clean. No wonder Catholics like going to confession.&#8221;</p>

<p>Exposing the limitations of a model also gives me a clearer picture of it, which makes it simpler for me to reason about, although it loses a superficial appearance of simplicity in the process. Perhaps a good analogy would be my taste for Arch Linux over Ubuntu. They both share the same basic architecture, but Ubuntu hides its warts with a simple interface, having the advantage of not initially confusing a user, but the disadvantage of masking the true cause of problems that do arise, while Arch intentionally exposes the inherent complexity of its architecture, perhaps making the learning curve steeper, but also making it easier to reason about due to its full disclosure. While Ubuntu has an abstraction which Arch lacks, it is not a good abstraction because it leaks, therefore the user must learn not only the abstraction but also how it works. Masking or failing to make clear the inherent flaws of a model, I think, is kind of like a leaky abstraction.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: conal</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comment-507</link>
		<dc:creator><![CDATA[conal]]></dc:creator>
		<pubDate>Tue, 24 Nov 2009 03:58:33 +0000</pubDate>
		<guid isPermaLink="false">http://conal.net/blog/?p=90#comment-507</guid>
		<description><![CDATA[&lt;p&gt;Thanks for the suggestions, Michael.
Mark Rafter made some detailed suggestions along these lines in March, which I&#039;d forgotten.
Worth picking up!&lt;/p&gt;
]]></description>
		<content:encoded><![CDATA[<p>Thanks for the suggestions, Michael.
Mark Rafter made some detailed suggestions along these lines in March, which I&#8217;d forgotten.
Worth picking up!</p>
]]></content:encoded>
	</item>
</channel>
</rss>
