<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>zig &amp;mdash; Sean Barnett</title>
    <link>https://seanbarnett.id.au/tag:zig</link>
    <description>Coffee, basketball, programming</description>
    <pubDate>Mon, 08 Jun 2026 13:36:41 +0000</pubDate>
    <image>
      <url>https://i.snap.as/FOPXss01.png</url>
      <title>zig &amp;mdash; Sean Barnett</title>
      <link>https://seanbarnett.id.au/tag:zig</link>
    </image>
    <item>
      <title>Roll Your Own Geometry</title>
      <link>https://seanbarnett.id.au/geometry?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[This post forms part of the ongoing #TagJob project.&#xA;&#xA;For reasons discussed in this post, I have tentatively decided to roll my own minimal geospatial types and calculations.!--more--&#xA;&#xA;There are many excellent Geospatial libraries available, even in the brave new frontier of an embryonic language such as Zig (thanks to the excellent bridging to C). Principal among these is Geos. However, I decided to move forward with my own geospatial types and calculations because:&#xA;&#xA;I want both visibility of and direct control over performance-related aspects such as memory management (more on this below)&#xA;I want the ability to tinker with algorithms that may favourably trade performance for functionality that is irrelevant to my use case&#xA;there&#39;s fun to be had&#xA;&#xA;My initial thinking was to define Zig structs for each basic geometry, but I have now moved to using simple aliases of native types and arrays:&#xA;&#xA;pub fn WithDimension(comptime dimension: comptimeint) type {&#xA;    return struct {&#xA;&#xA;        pub const Vector = @Vector(dimension, f64);&#xA;        pub const Coordinate = [dimension]f64;&#xA;        pub const Coordinates = []Coordinate;&#xA;        pub const Point = Coordinate;&#xA;        pub const Line = [2]Coordinate;&#xA;        pub const LineString = Coordinates;&#xA;        pub const LinearRing = Coordinates;&#xA;        pub const Polygon = []LinearRing;&#xA;&#xA;        pub const AnyGeometry = union(enum) {&#xA;            point: Point,&#xA;            line: Line,&#xA;            lineString: LineString,&#xA;            linearRing: LinearRing,&#xA;            polygon: Polygon,&#xA;        };&#xA;&#xA;        pub const Envelope = struct {&#xA;            min: Vector, // stored as vector as heavily used with SIMD&#xA;            max: Vector, // stored as vector as heavily used with SIMD&#xA;        };&#xA;&#xA;    };&#xA;}&#xA;This design decision does mean that I cannot add either additional state or instance functions to my types. But I do avoid any overhead of allocating an instance of the wrapper type, and of needlessly creating instances of the wrapper type simply to call a function that is interested in the internal state only.&#xA;&#xA;In place of instance methods, I&#39;ll be using a separate calculator that accepts each geometry type. Again, I&#39;ll claim a win here because I can use different calculation functions (e.g. Euclidean versus Geodesic distance) without coupling either to the type.&#xA;&#xA;The one exception is the Envelope type, for which I have wrapped min and max points, and a small number of instance functions:&#xA;&#xA;valid&#xA;intersects&#xA;covers&#xA;intersectionOf&#xA;unionOf&#xA;bufferOf&#xA;&#xA;Within Envelope I am storing min and max as Vectors as most functions use SIMD, my first foray into this world:&#xA;/// Determine if this envelope and another envelope intersect.&#xA;pub fn intersects(self: @This(), other: @This()) bool {&#xA;    const resultminmax = self.min &lt;= other.max;&#xA;    const resultmaxmin = other.min &lt;= self.max;&#xA;    const rval = @reduce(.And, resultminmax &amp; resultmax_min);&#xA;    return rval;&#xA;}&#xA;&#xA;The code lives here.&#xA;&#xA;Tags: #TagJob #Geospatial #Zig #SIMD]]&gt;</description>
      <content:encoded><![CDATA[<p><em>This post forms part of the ongoing <a href="https://seanbarnett.id.au/tag:TagJob" class="hashtag"><span>#</span><span class="p-category">TagJob</span></a> project.</em></p>

<p>For reasons discussed in this post, I have tentatively decided to roll my own minimal geospatial types and calculations.</p>

<p>There are many excellent Geospatial libraries available, even in the brave new frontier of an embryonic language such as Zig (thanks to the excellent bridging to C). Principal among these is <a href="https://libgeos.org">Geos</a>. However, I decided to move forward with my own geospatial types and calculations because:</p>
<ul><li>I want both visibility of and direct control over performance-related aspects such as memory management (more on this below)</li>
<li>I want the ability to tinker with algorithms that may favourably trade performance for functionality that is irrelevant to my use case</li>
<li>there&#39;s fun to be had</li></ul>

<p>My initial thinking was to define Zig structs for each basic geometry, but I have now moved to using simple aliases of native types and arrays:</p>

<pre><code>pub fn WithDimension(comptime dimension: comptime_int) type {
    return struct {

        pub const Vector = @Vector(dimension, f64);
        pub const Coordinate = [dimension]f64;
        pub const Coordinates = []Coordinate;
        pub const Point = Coordinate;
        pub const Line = [2]Coordinate;
        pub const LineString = Coordinates;
        pub const LinearRing = Coordinates;
        pub const Polygon = []LinearRing;

        pub const AnyGeometry = union(enum) {
            point: Point,
            line: Line,
            lineString: LineString,
            linearRing: LinearRing,
            polygon: Polygon,
        };

        pub const Envelope = struct {
            min: Vector, // stored as vector as heavily used with SIMD
            max: Vector, // stored as vector as heavily used with SIMD
        };

    };
}
</code></pre>

<p>This design decision does mean that I cannot add either additional state or instance functions to my types. But I do avoid any overhead of allocating an instance of the wrapper type, and of needlessly creating instances of the wrapper type simply to call a function that is interested in the internal state only.</p>

<p>In place of instance methods, I&#39;ll be using a separate calculator that accepts each geometry type. Again, I&#39;ll claim a win here because I can use different calculation functions (e.g. Euclidean versus Geodesic distance) without coupling either to the type.</p>

<p>The one exception is the Envelope type, for which I have wrapped min and max points, and a small number of instance functions:</p>
<ul><li>valid</li>
<li>intersects</li>
<li>covers</li>
<li>intersectionOf</li>
<li>unionOf</li>
<li>bufferOf</li></ul>

<p>Within Envelope I am storing min and max as Vectors as most functions use SIMD, my first foray into this world:</p>

<pre><code>/// Determine if this envelope and another envelope intersect.
pub fn intersects(self: @This(), other: @This()) bool {
    const result_min_max = self.min &lt;= other.max;
    const result_max_min = other.min &lt;= self.max;
    const rval = @reduce(.And, result_min_max &amp; result_max_min);
    return rval;
}
</code></pre>

<p>The code lives <a href="https://bitbucket.org/tagsoftware/tagjobspatial">here</a>.</p>

<p>Tags: <a href="https://seanbarnett.id.au/tag:TagJob" class="hashtag"><span>#</span><span class="p-category">TagJob</span></a> <a href="https://seanbarnett.id.au/tag:Geospatial" class="hashtag"><span>#</span><span class="p-category">Geospatial</span></a> <a href="https://seanbarnett.id.au/tag:Zig" class="hashtag"><span>#</span><span class="p-category">Zig</span></a> <a href="https://seanbarnett.id.au/tag:SIMD" class="hashtag"><span>#</span><span class="p-category">SIMD</span></a></p>
]]></content:encoded>
      <guid>https://seanbarnett.id.au/geometry</guid>
      <pubDate>Mon, 08 Jun 2026 06:07:47 +0000</pubDate>
    </item>
  </channel>
</rss>