On-Demand 3D-Printed Parts

    We often hear the promise that advances in 3D printing will provide a new supply chain model, giving us access to products for which there is otherwise insufficient demand; that, amongst other things, this is set to revolutionise the spare parts market, allowing companies to provide parts for any item on-demand.

    Unfortunately, although companies such as Shapeways offer comprehensive marketplaces for makers to sell their designs, these are flooded with novelty items (and gadget cases): items of which there is already a glut in traditional retail channels.

    You can imagine my excitement then when I recently had an opportunity to purchase a vaguely practical 3D-printed item which would not have otherwise been available:

    3DS Cartridge Blank Thin

    Having switched to all digital downloads, my Nintendo 3DS now has an empty cartridge slot: ideal for collecting dust and a perfect point of structural weakness. Thanks to 3D printing, it's possible to purchase a blank 3DS cartridge from Shapeways. I opted for the 'Black Strong & Flexible' material – a matte textured plastic – and an very pleased with the result.


    Game Boy Cartridge

    In the interests of restoring to Game Play Color some of the original Game Play design that didn't make the first-cut, I've been gradually converting these elements over to pure HTML and CSS.

    One piece I found particularly daunting was the rendering of the original Game Boy cartridge used in the game library. The current version of Game Play Color sports a much simpler game library for this very reason.

    On the left you can see the more skeuomorphic Game Play game library; on the right, the simpler Game Play Color game library.

    This has always felt like the lazy approach (and I'm not a fan of exclusively flat design) so, a few days ago, I set about rectifying the situation:

    Super Mario Land
    This Side Out

    The HTML for this is fairly simple, though I'm disappointed by the number of elements required, especially for the embossed bars across the top of the cartridge:

    <div class="cartridge">
        <div class="top"></div>
        <div class="logo"></div>
        <div class="lines">
            <div class="bar left one"></div>
            <div class="bar left two"></div>
            <div class="bar left three"></div>
            <div class="bar left four"></div>
            <div class="bar right one"></div>
            <div class="bar right two"></div>
            <div class="bar right three"></div>
            <div class="bar right four"></div>
        </div>
        <div class="inset">
            <div class="label">
                <div class="title right">Super Mario Land</div>
                <div class="title left">This Side Out</div>
                <img src="/assets/2016/03/mario.jpg" />
            </div>
        </div>
        <div class="edge left"></div>
        <div class="edge right"></div>
        <div class="arrow"></div>
    </div>
    

    Unfortunately, the CSS proves a little more complex, with many special cases and absolute dimensions:

    .cartridge {
        width: 140px;
        height: 152px;
        background-color: #848086;
        position: relative;
        box-shadow:
            0 0 5px rgba(0, 0, 0, 0.6),
            inset 0 1px 1px rgba(255, 255, 255, 0.6);
        margin-top: 8px;
        margin: auto;
    }
    
    .cartridge > .logo {
        box-sizing: border-box;
        position: absolute;
        top: 4px;
        left: 15px;
        width: 110px;
        height: 28px;
        z-index: 10;
        border-radius: 100px;
        background: linear-gradient(top,
                                    rgba(0, 0, 0, 0.2),
                                    rgba(255, 255, 255, 0.2));
        background: -moz-linear-gradient(top,
                                         rgba(0, 0, 0, 0.2),
                                         rgba(255, 255, 255, 0.2));
        background: -webkit-linear-gradient(top,
                                            rgba(0, 0, 0, 0.2),
                                            rgba(255, 255, 255, 0.2));
        box-shadow:
            0 1px 1px rgba(255, 255, 255, 0.5),
            inset 0 1px 1px rgba(0, 0, 0, 0.5);
    }
    
    .cartridge > .top {
        box-sizing: border-box;
        position: absolute;
        content: "";
        top: -8px;
        left: 0;
        background-color: #848086;
        height: 10px;
        box-shadow:
            inset 0 1px 1px rgba(255, 255, 255, 0.6);
        width: 90%;
    }
    
    .cartridge > .arrow {
        box-sizing: border-box;
        content: "";
        position: absolute;
        left: 60px;
        bottom: 4px;
        width: 19px;
        height: 12px;
        border-top: 1px solid rgba(0, 0, 0, 0.3);
    }
    
    .cartridge > .arrow:before, .cartridge > .arrow:after {
        content: "";
        width: 14px;
        height: 0;
        border-top: 1px solid rgba(255, 255, 255, 0.3);
        position: absolute;
        top: 4px;
    }
    
    .cartridge > .arrow:before {
        transform: rotate(45deg);
        -webkit-transform: rotate(45deg);
        left: -2px;
    }
    
    .cartridge > .arrow:after {
        transform: rotate(-45deg);
        -webkit-transform: rotate(-45deg);
        left: 7px;
    }
    
    .cartridge > .inset {
        content: "";
        position: absolute;
        bottom: 20px;
        left: 15px;
        height: 92px;
        width: 110px;
        box-sizing: border-box;
        border-radius: 3px;
        box-shadow:
            0 1px 1px rgba(255, 255, 255, 0.5),
            inset 0 1px 1px rgba(0, 0, 0, 0.5);
        padding: 2px;
    }
    
    .cartridge > .inset > .label {
        width: 100%;
        height: 100%;
        background: linear-gradient(top,
                                    rgba(200, 200, 200, 1.0),
                                    rgba(170, 170, 170, 1.0));
        background: -moz-linear-gradient(top,
                                         rgba(200, 200, 200, 1.0),
                                         rgba(170, 170, 170, 1.0));
        background: -webkit-linear-gradient(top,
                                            rgba(200, 200, 200, 1.0),
                                            rgba(170, 170, 170, 1.0));
        text-align: center;
        border-radius: 1px;
        box-shadow: inset 0 0 2px rgba(255, 255, 255, 0.4);
        position: relative;
    }
    
    .cartridge > .inset > .label > .title {
        position: absolute;
        top: 40px;
        font-size: 6px;
        text-transform: uppercase;
        font-family: Helvetica;
        width: 106px;
        text-align: center;
    }
    
    .cartridge > .inset > .label > .title.left {
        transform: rotate(-90deg);
        -webkit-transform: rotate(-90deg);
        left: 45%;
    }
    
    .cartridge > .inset > .label > .title.right {
        transform: rotate(90deg);
        -webkit-transform: rotate(90deg);
        left: -45%;
    }
    
    .cartridge > .inset > .label > img {
        height: 100%;
    }
    
    .cartridge > .edge {
        box-sizing: border-box;
        position: absolute;
        bottom: 0;
        width: 4px;
        height: 86px;
        border-top: 1px solid rgba(255, 255, 255, 0.3);
    }
    
    .cartridge > .edge.left {
        left: 0;
        background: linear-gradient(left,
                                    rgba(0, 0, 0, 0.4),
                                    rgba(0, 0, 0, 0.2));
        background: -moz-linear-gradient(left,
                                         rgba(0, 0, 0, 0.4),
                                         rgba(0, 0, 0, 0.2));
        background: -webkit-linear-gradient(left,
                                            rgba(0, 0, 0, 0.4),
                                            rgba(0, 0, 0, 0.2));
    }
    
    .cartridge > .edge.right {
        right: 0;
        background: linear-gradient(right,
                                    rgba(0, 0, 0, 0.4),
                                    rgba(0, 0, 0, 0.2));
        background: -moz-linear-gradient(right,
                                         rgba(0, 0, 0, 0.4),
                                         rgba(0, 0, 0, 0.2));
        background: -webkit-linear-gradient(right,
                                            rgba(0, 0, 0, 0.4),
                                            rgba(0, 0, 0, 0.2));
    }
    
    .cartridge > .lines {
        box-sizing: border-box;
        position: absolute;
        width: 100%;
        height: 30px;
        top: 6px;
        left: 0;
        overflow: hidden;
    }
    
    .cartridge > .lines > .bar {
        box-sizing: border-box;
        position: absolute;
        width: 20px;
        height: 4px;
        box-shadow:
            0 1px 1px rgba(255, 255, 255, 0.5),
            inset 0 1px 1px rgba(0, 0, 0, 0.5);
    }
    
    .cartridge > .lines > .bar.left {
        left: -4px;
    }
    
    .cartridge > .lines > .bar.right {
        right: -4px;
    }
    
    .cartridge > .lines > .bar.one {
        top: 0;
    }
    
    .cartridge > .lines > .bar.two {
        top: 7px;
        width: 16px;
    }
    
    .cartridge > .lines > .bar.three {
        top: 14px;
        width: 16px;
    }
    
    .cartridge > .lines > .bar.four {
        top: 21px;
    }
    
    .cartridge > .lines > .bar.left.one,
    .cartridge > .lines > .bar.right.four {
        transform: skew(-55deg);
    }
    
    .cartridge > .lines > .bar.left.two,
    .cartridge > .lines > .bar.right.three {
         transform: skew(-12deg);   
    }
    
    .cartridge > .lines > .bar.left.three,
    .cartridge > .lines > .bar.right.two {
        transform: skew(12deg);
    }
    
    .cartridge > .lines > .bar.left.four,
    .cartridge > .lines > .bar.right.one {
        transform: skew(55deg);
    }
    

    Generally, I find the overall result fairly pleasing and, adding a few tweaks for Game Boy Color cartridges games helps keep things playful:


    Charging The Dash

    I recently acquired The Dash from Bragi. They're a great pair of headphones and a fantastic first product from Bragi, modulo all the usual Kickstarter caveats1.

    One of the many features of The Dash is the ability to load music directly onto the headphones themselves – they mount as a USB mass storage device when plugged into a computer and adding music is simply a matter of dragging-and-dropping unencrypted music files.

    As useful as this is, it's far more common that I simply wish to charge my headphones when plugging them into MacBook Pro. Since OS X helpfully auto-mounts USB mass storage volumes, I find myself with many of these when I'm done charging:

    Fortunately, it's possible to disable auto-mounting in OS X on a per-disk basis by tweaking your fstab; Wolf Paulus has a great post describing the basics of how to do this. The technique described relies on using the Volume UUID to identify the device but, since The Dash don't appear to have a Volume UUID, it's necessary to filter using the Volume Label instead2:

    #                                                                                      
    # Warning - this file should only be modified with vifs(8)                             
    #                                                                                      
    # Failure to do so is unsupported and may be destructive.                              
    #                                                                                      
    LABEL=THE\040DASH none msdos rw,noauto 0 0
    

    Remember to use vifs to modify your fstab, instead of editing it directly:

    $ sudo vifs
    

    1. Over-promise and under-deliver. In the case of The Dash, some of the additional functionality such as fitness tracking (and even voice calling) doesn't yet work as advertised.
    2. Thanks to Johannes WeiƟ for his suggestion and guidance.

    More Psion User Scans

    Over Christmas, I managed to find the time to scan two more editions of Psion User. I've now cleaned up the first of these – Winter 1998/99 – and added it to archive.org.

    This edition served to introduce the 'pearlescent' aqua green special edition Series 5, a device I coveted at the time. I finally satisfied that childhood desire by purchasing one on eBay late last year, adding to a growing collection of Psion palmtops.

    Unfortunately, Psion's interest in Psion User was already waning at this point and, at only 8 pages, it's significantly shorter than previous editions.

    You can find a direct download here.


    Adding Timestamps to Terminal Commands

    During certain stages of our software development cycle, I find myself wondering exactly when I ran a given command – did I start that UI process or task before, or after, I ran that command line utility?

    More often than not, I give up guessing and re-run both jobs just in case. Scroll-back helps to some extent, but it's infuriating to see that you ran a command, yet still not know when you ran it.

    In an attempt to improve on this, I've modified my prompt to include a right-aligned date, effectively adding a timestamp to the previous completed command:

    If you're interested in using this yourself, you can add all or some of the following to your ~/.bash_profile:

    function right_aligned_date {
        current_date=\[$( date )\]
        width=$( tput cols )
        echo "$( printf %${width}s "${current_date}" )"
    }
    
    PS1="\[\033[0;37m\]\$( right_aligned_date )\[\033[0m\]\n\u@\h \W $ "
    

    Update: Some utilities – such as Python's virtualenv – will modify the $PS1 to add additional information about the current environment. This can break the date alignment used in the above example as it relies on spaces to position the date.

    The better solution is to explicitly set the column start position as follows:

    function right_aligned_date {
        current_date=$( date "+%H:%M:%S %Z" )
        column=$( expr $( tput cols ) - ${ #current_date } + 1 )
        printf "\e[${column}G${current_date}"
    }