Skip to content

Instantly share code, notes, and snippets.

@librasteve
Created December 15, 2021 12:40
Show Gist options
  • Save librasteve/7f712d15d9c635b58dc515fe9261437c to your computer and use it in GitHub Desktop.
Save librasteve/7f712d15d9c635b58dc515fe9261437c to your computer and use it in GitHub Desktop.

#Santa and the Magic Tree (M-Tree)

It was Christmas Eve in the Workhouse and Santa was getting worried about how the weight of all those presents would reduce the height of his sled's flighpath. Would he be able to clear the height of the tallest Christmas trees on his worldwide journey?

He asked one of his helpers to whip up a quick script to see how much danger he would be in and in stepped p6elf to do the job.

Naturally p6elf (pronounced "Physics Elf") wanted to show off his raku skills to grow his reputation with Santa, the reindeer and all the other elves (knowing how much Santa is into the whole raku thing). So he started by picking up two modules and mixing them together. To share with the others so that they could see how they could easily make their own models so he used Brian Duggan's cool Jupyter notebook module.

#This setup cell inherits from the Math::Polygons classes such as Point and Triangle provided by Math::Polygons and overrides their plain Numeric variables with Physics::Measure classes of Length and Area.

use Math::Polygons;
use Physics::Measure :ALL;

$Physics::Measure::round-val = 1;

class M-Point is Point {
    has Length $.x;
    has Length $.y;
}

class M-Polygon is Polygon {
    has M-Point @.points;
}

class M-Rectangle is Rectangle {
    has M-Point  $.origin;
    has Length   $.width;
    has Length   $.height;

    method area( --> Area ) {
        $!height * $!width
    }
}

class M-Triangle is Triangle {
    has M-Point $.apex is required;
    has Length  $.side is required;

    method height( --> Length ) {
        sqrt($!side**2 - ($!side/2)**2)
    }
    method area( --> Area ) {
        ( $.height * $!side ) / 2
    }
}

That was quick, he thought, the raku OO module really is cool and concise and just seamlessly applies classes as types to check the correctness of my work.

p6elf then went back on the keys to use his new classes and get to a model tree...

my $tri1 = M-Triangle.new(stroke => "green", fill => "green",
                apex => M-Point.new(100m, 50m),
                side => 50m,
            );
my $tri2 = M-Triangle.new(stroke => "green", fill => "green",
                apex => M-Point.new(100m, 75m),
                side => 75m,
            );
my $tri3 = M-Triangle.new(stroke => "green", fill => "green",
                apex => M-Point.new(100m, 100m),
                side => 100m,
            );
my $rect  = M-Rectangle.new(stroke => "brown", fill => "brown",
                origin  => M-Point.new(90m, 185m),
                width   => 20m,
                height  => 40m,
            );

my @elements =  [ $tri1, $tri2, $tri3, $rect ];

say "Tree Height is ", [+] @elements.map(*.height);
say "Tree Area is ",   [+] @elements.map(*.area);

my $tree = Group.new( :@elements );
my $drawing = Drawing.new( elements => $tree );
$drawing.serialize.say;

Wowee, look how I can just type in 100m and the Physics::Measure postfix operator magically makes a Length object ... no need to repetitively type in my Length $d = Length.new(value => 100, units => 'm'); every time (provided I have an SI unit / SI prefix such as cm, kg, ml and so on). And, like magic, a beautiful Xmas tree appeared on the screen.

IMAGE 1

Then p6elf realised his mistake. While Santa would need to fly over the towering trees that surround the North Pole - where sizes are measured in metric units, he would also need to deliver many, many presents to the kids in America - and there tree are purchased by the foot. Of course, raku to the rescue - since p6elf was too lazy to retype all the embedded unit values in his first model, he created a Magic Tree (M-Tree) class and parameterized the dimensions of the elements like this:

class M-Tree {
    has M-Point $.apex;
    has Length  $.size;

    has M-Triangle  $!top;
    has M-Triangle  $!middle;
    has M-Triangle  $!bottom;
    has M-Rectangle $!base;

    method elements {
        [ $!top, $!middle, $!bottom, $!base ]
    }
    method height( --> Length ) {
        [+] $.elements.map(*.height)
    }
    method area( --> Area ) {
        [+] $.elements.map(*.area)
    }

    method TWEAK {
        my $stroke := my $fill;
        $fill = "green";

        #calculate x co-ords relative to top of drawing, according to height
        my \x := $!apex.x;
        my \s := $!size;
        my \p = [ (s / 4) , ( s * 3/8), (s / 2) ];

        $!top    = M-Triangle.new( :$stroke, :$fill,
                                   apex   => M-Point.new(x, p[0]),
                                   side   => p[0]    );
        $!middle = M-Triangle.new( :$stroke, :$fill,
                                   apex   => M-Point.new(x, p[1]),
                                   side   => p[1]    );
        $!bottom = M-Triangle.new( :$stroke, :$fill,
                                   apex   => M-Point.new(x, p[2]),
                                   side   => p[2]    );

        $fill   = "brown";
        $!base  = M-Rectangle.new( :$stroke, :$fill,
                    origin  => M-Point.new(( 0.9 * x ), (([+] p) - (0.2 * s))),
                                   width   => 0.1 * s,
                                   height  => 0.2 * s );
    }

}

#my $size = 200m;
my $size = ♎️'50 ft';

my M-Point $apex    .= new(($size / 2), ($size / 4));
my M-Tree  $us-tree .= new(:$apex, :$size);

say "Tree Height is {$us-tree.height} (including the base)";
say "Tree Area is {$us-tree.area}";

my $drawing = Drawing.new( elements => $us-tree.elements );
$drawing.serialize.say;

Look how cool programming is, I can capture the shape of my object and just need to set the control dimension $size in one place. Instead of 200m via the postfix syntax,I can use the libra emoji prefix<♎> that uses powerful raku Grammars to read the units and automatically convert. Let's take a look at the result:

IMAGE2

No need to worry, Santa can easily get over these smaller forests even when the reindeer are tired and low on energy...

... energy - hmmm maybe I can use raku Physics::Measure to work out how much energy we need to load up in terajoules(TJ) and then convert that to calories to work out how much feed Rudy and the team will need ... mused p6elf in a minced pie induced dream.

~p6steve.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment