Saturday 27 February 2021

The Unknotted Rope Analogy

A rope is a wonderful piece of crude machinery. It is designed to support great loads. With the right series of knots, it can fasten, hitch, bind and stop, among other things. There is a great deal of functionality within the humble rope if you know the right knot to use.

Yet, knots come with a caveat - they weaken the rope.

Too many knots.

A rope can support up to a certain amount of strain before it snaps. This is what is known as Breaking Strength. Knots lower the Breaking Strength of a rope... and the more knots are tied into a rope, the weaker it becomes.

And so it is with programming.

Code increases the functionality of a program. With more code, your program can achieve more things. But code also weakens the program. The more code you write, the more moving parts your program has, the more bugs and vulnerabilities inevitably work their way into your program.

For example...

Does your program connect to a database? Then that database connection can be hacked.

Does your program perform division? Then there is a possibility of a divide-by-zero exception.

Does your program perform string operations? Then there is a multitude of string-specific quirks you have to watch out for, such as character sets and special characters.

What's the solution, then?

Yikes, you were expecting a solution? This was only ever meant to be an analogy.

Kids, there is no solution. Unless you are writing a Hello World program, there is going to be the possibility of bugs. No two ways about it.

A Hello World program is like a single unknotted length of rope - beautiful, pristine, problem-free... and utterly useless on its own. Nothing useful was ever accomplished with a rope that had no knots tied in it, unless said usefulness amounted to acting as dead weight. Even if all you want to do with a rope is climb it, that rope has to be anchored to something else... usually using a knot of some kind.

But...!

There is, of course, a way to mitigate those risks. While we do not have to make our ropes or programs completely useless, we can limit the functionality of these things. A rope can be used to do one thing, while other ropes can be deployed to take care of other things. That way we won't have a case of one single rope handling multiple simultaneous uses.

Likewise, a program needs do do one thing. And any additional functionality can be outsourced to other components.

Knot a problem!
T___T

Sunday 21 February 2021

Reference Review: How To Kill The Scrum Monster

Often, it is not enough to simply experience something. To truly digest the experience, it is advisable to familiarize oneself with the theory behind that experience. The past decade of my career, saw me experience firsthand several interesting styles of Management, and work practices. And I didn't want the experience to be wasted by my ignorance of the underlying concepts.

During a rare investigation into tech practices - Agile practices, in particular - I found myself reading some material - one of them the evocatively-titled How to kill the Scrum Monster by Ilya Bibik.

This book is billed as a quick guide to the role of the Scrum Master and Agile Methodology. The author has spent a respectable amount of time as a Scrum Master, and attempts to condense his experience in an educational format, explaining what the Agile methodology is in tech, and the role of the Scrum Master in that context.

The Premise

Things start off with an overview of Agile methodologies, Scrum in particular. This segues into an examination of the role of the Scrum Master, and what it involves.

The Aesthetics

I'll be honest - I judged this book by its cover. There's just something intensely relatable about defining Scrum as some kind of monster that needs to be killed. The layout of its contents, however, is pretty academic in nature. And by that, I mean it reads like a lecture rather than a fantasy novel, with many things that would not look out of place in a textbook. That's by no means a bad thing, however.

The Experience

This is a mercifully fast read. It is intended to be easy to digest. While the author isn't exactly the most skilled in the English language, if you can look past that particular flaw, there is a fair amount of knowledge to be found within these pages.

The Interface

The graphs and charts are not all that hard to understand. In fact, they can be safely ignored. Chapters and sections are well-structured. It's been kept as simple as possible.

What I liked

The explanations behind the terminology used in Agile, and the underlying concepts, are pretty adequate, and potentially useful if you're reading this without any idea of what Agile is.

This book does not just cover the role of the Scrum Master. It branches into other hybrid roles such as Scrum Master/Developer and Scrum Master/Product Owner. It also examines the relation of the Scrum Master to other members of an Agile team.

There is a section devoted entirely to case studies, scenarios and proposed solutions. Not only does the author put forward scenarios that feel real, he even takes the trouble to explain the principles and politics behind the solutions proposed.

What I didn't

The copywriting, editing and general use of grammar are awful. In some places, words are spelled wrongly and sentences are awkwardly structured. It's pervasive and glaring enough that this is a real sticking point.

There's one particular spot that sticks out like a sore thumb. In the section where inter-team conflicts are discussed, the author describes one type of conflict as "pure evil". This is quite unprofessional; and jarring precisely because for the most part in the book, he has successfully avoided the use of such emotive superlatives and kept his language neutral. Talk about mood whiplash!

The book is not a particularly interesting read. This may be due to the author's lack of proficiency in the English language. He's certainly earnest enough, but one senses that he's no salesman.

Many of the charts seem superfluous. In some places they can be so simplistic that one suspects that they were placed there as filler.

Conclusion

Ilya Bibik's work isn't terrible. As an informative source, it has its uses. For academic purposes, it does serve as a quick guide. It's just hard to read if you're particular about the English language. For starters, one could certainly do worse.

However, if you're looking for a well-written book, you may want to look elsewhere.

My Rating

5.5 / 10

Stay Scrum-tious!
T___T

Tuesday 16 February 2021

Spot The Bug: Python Data Mislocation

Yo, you delightful bug-hunters. I'm pleased to bring to you a whole new edition of Spot The Bug!

Bugs, beware!


Today's subject is Python. Yes, His Teochewness has been tinkering yet again with forces beyond his ken, and a whole ton of pesky bugs is the result! Well, I exaggerate - just the one.

Python is a good language for data analysis, and I had data to analyze. Specifically, COVID-19 data. Since April of last year, I've been collecting data from the daily updates sent to me by the Singapore Government in the hopes of being able to derive some meaningful insight as to the trends regarding this disease.

Using the Python Pandas library, I managed to read the data for April 2020 into a dataframe.
import pandas as pd

df = pd.read_csv('covid_apr2020.csv')
df.set_index('RowNum', inplace = True)
df


This is what it looked like.



Just trying to plot a quick line chart here, to see the number of migrant worker infections, "MW" versus the number of Singapore citizen infections, "SG".
import pandas as pd

df = pd.read_csv('covid_apr2020.csv')
df.set_index('RowNum', inplace = True)
df

df.plot.line('Day', ['MW', 'SG'])



And there it was.

However, I wanted a small subset of that data. I wanted to take data from every seven days instead of every day. So this is what I did - I created a new dataframe, df_weekly, by taking only rows 7, 14, 21 and 28. Here, I also commented out the plotting function in order to see what kind of data I would get.
import pandas as pd

df = pd.read_csv('covid_apr2020.csv')
df.set_index('RowNum', inplace = True)
df
df_weekly = df.iloc[[7, 14, 21, 28]]
df_weekly


#df.plot.line('Day', ['MW', 'SG'])


What went wrong

This is the data I got... and it certainly wasn't what I wanted. Why was the data starting from 8th April instead of 7th April? I was getting data from 8th, 15th, 22nd and 29th instead.

Why it went wrong

You see, in programming, counts start from 0. And using the iloc() method meant that Python was using 7, 14, 21 and 28 to denote row numbers. While my row numbers (the RowNum column) started nicely from 1, as far as Python was concerned, row number 7 was actually the 8th row in the dataframe. Row number 14 was actually the 15th row. And so on.

How I fixed it

Instead of using the iloc() method, I used the loc() method. Now Python would use those numbers as a reference to the index of the dataframe (the "RowNum" column)
import pandas as pd

df = pd.read_csv('covid_apr2020.csv')
df.set_index('RowNum', inplace = True)
df
df_weekly = df.loc[[7, 14, 21, 28]]
df_weekly

#df.plot.line('Day', ['MW', 'SG'])


Now the data looked correct.


And to test my data, now I reactivated the last line and changed it to plot for df_weekly instead.
import pandas as pd

df = pd.read_csv('covid_apr2020.csv')
df.set_index('RowNum', inplace = True)
df
df_weekly = df.loc[[7, 14, 21, 28]]
df_weekly

df_weekly.plot.line('Day', ['MW', 'SG'])


The final chart!


Moral of the story

Some functions look similar but work very differently. The confusion between loc() and iloc() isn't the first time a novice Python programmer has been caught out, and won't be the last.

Remember, the "i" in iloc() stands for integer. So when we use iloc(), Python will search for rows by integer value instead of string reference to the index.

I've got my i on you!
T___T

Wednesday 10 February 2021

Web Tutorial: CNY 2021 and Valentine's Day Animation (Part 2/2)

Now that we've created an ox head and made a simple animation, let's build on that. Add this to your styles. It will set font. I prefer a nice orange myself.
svg
{
    width: 800px;
    height: 550px;
    background-color: rgba(200, 200, 200, 0);
}

g.text
{
    stroke-width: 1px;
    font: 20px Georgia;
    fill: rgba(255, 100, 0, 0);
}


g.light
{
    fill: rgba(255, 100, 0, 0.5);
}


Now add a g tag at the end of the svg. The x and y attributes have been filled out for you; this will differ based on your choice of font.
        <path d="M340 355 q0 20 -20 30 Z"></path>
        <path d="M460 355 q0 20 20 30 Z"></path>
    </g>

    <g class="text">
        <text x="250" y="200">
            year of the
        </text>
        <text x="450" y="200">
            X
        </text>
        <text x="320" y="220">
            valentine's day 2021
        </text>
    </g>
</svg>


Now if you refresh, you won't see a damn thing because the alpha aspect of the fill attribute has been set to 0, which is zero opacity. We are going to animate this. In this case, we animate the g tag instead of the text itself. The effect is that all the text within that g tag gets animated. We animate the fill property, setting it to full opacity at the end of the transformation.
<g class="text">
    <text x="250" y="200">
        year of the
    </text>
    <text x="450" y="200">
        X
    </text>
    <text x="320" y="220">
        valentine's day 2021
    </text>

    <animate
        id="animateTextAppear"
        attributeName="fill"
        from="rgba(255, 0, 0, 0)"
        to="rgba(255, 100, 0, 1)"
        fill="freeze"
        begin="0s"
        dur="2.5s"
        repeatCount="1"
    />

</g>


And the text fades into view. Well done! Don't worry about the missing "o" in "ox". We will be filling that in.



Now, what would Valentine's Day be without a heart? We'll create one. Notice that the horns are kind of heart-shaped. That's deliberate. Our heart will originate from there. Add this styling. The heart will have a nice red fill.
    g.dark
    {
        fill: rgba(100, 0, 0, 1);
    }

    g.heart
    {
        fill: rgba(255, 0, 0, 1);
    }

</style>


Since we want the heart to appear behind the horns, we'll need to code the g tag right at the top of the SVG.
<svg>
    <g class="heart">
        <path d="M400 285 q-120 -60 0 35 q120 -100 0 -35 Z">
    </g>


    <g class="dark">
        <path d="M380 320 l-30 5 q-80 -110 40 -50 q-100 -40 0 45 Z"></path>
        <path d="M420 320 l30 5 q80 -110 -40 -50 q100 -40 0 45 Z"></path>
    </g>


Oooh. Nice.



Now hide the heart by doing this. We're going to make it fade into view like we did for the text.
g.heart
{
    fill: rgba(255, 0, 0, 0);
}


Now add an animate tag in there. Give it an id of animateHeartAppear, and pretty much do what we did for the text. Refresh. Does it fade into view like a good little heart? Excellent.
<g class="heart">
    <path d="M400 285 q-120 -60 0 35 q120 -100 0 -35 Z">
        <animate
            id="animateHeartAppear"
            attributeName="fill"
            from="rgba(255, 0, 0, 0)"
            to="rgba(255, 0, 0, 1)"
            fill="freeze"
            begin="2s"
            dur="1.5s"
            repeatCount="1"
        />
    </path>
</g>


We follow up with another animation with an id of animateHeartExpand. This begins when animateHeartAppear ends. We animate the d attribute, making the heart appear to stretch upwards.
<animate
    id="animateHeartAppear"
    attributeName="fill"
    from="rgba(255, 0, 0, 0)"
    to="rgba(255, 0, 0, 1)"
    fill="freeze"
    begin="2s"
    dur="1.5s"
    repeatCount="1"
/>

<animate
    id="animateHeartExpand"
    attributeName="d"
    from="M400 285 q-120 -60 0 35 q120 -100 0 -35 Z"
    to="M400 120 q-120 -60 0 120 q120 -180 0 -120 Z"
    fill="freeze"
    begin="animateHeartAppear.end"
    dur="2s"
    repeatCount="1"
/>


See what I mean?!



Next up is animateHeartContract, which begins as animateHeartExpand ends. This one brings the heart back to more normal proportions.
<animate
    id="animateHeartExpand"
    attributeName="d"
    from="M400 285 q-120 -60 0 35 q120 -100 0 -35 Z"
    to="M400 120 q-120 -60 0 120 q120 -180 0 -120 Z"
    fill="freeze"
    begin="animateHeartAppear.end"
    dur="2s"
    repeatCount="1"
/>

<animate
    id="animateHeartContract"
    attributeName="d"
    from="M400 120 q-120 -60 0 120 q120 -180 0 -120 Z"
    to="M400 150 q-120 -60 0 100 q120 -150 0 -100 Z"
    fill="freeze"
    begin="animateHeartExpand.end"
    dur="0.5s"
    repeatCount="1"
/>


Great...



Now for two more animate tags. The first one is animateHeart1 and it begins after animateHeartContract ends, and also after animateHeart2 ends. The second is animateHeart2, and it begins after animateHeart1 ends. Thus animateHeart1 and animateHeart2 have a bit of a circular thing going on. It's meant to make the heart pulsate, sort of. Try it!
<animate
    id="animateHeartContract"
    attributeName="d"
    from="M400 120 q-120 -60 0 120 q120 -180 0 -120 Z"
    to="M400 150 q-120 -60 0 100 q120 -150 0 -100 Z"
    fill="freeze"
    begin="animateHeartExpand.end"
    dur="0.5s"
    repeatCount="1"
/>

<animate
    id="animateHeart1"
    attributeName="d"
    from="M400 150 q-125 -60 0 100 q125 -150 0 -100 Z"
    to="M400 150 q-130 -60 0 90 q110 -150 0 -90 Z"
    fill="freeze"
    begin="animateHeartContract.end;animateHeart2.end"
    dur="1.5s"
    repeatCount="1"
/>


<animate
    id="animateHeart2"
    attributeName="d"
    from="M400 150 q-130 -60 0 90 q110 -150 0 -90 Z"
    to="M400 150 q-125 -60 0 100 q125 -150 0 -100 Z"
    fill="freeze"
    begin="animateHeart1.end"
    dur="1.5s"
    repeatCount="1"
/>


But the thing is, the heart still doesn't really resemble the "o" in "ox". So let's give it a little center. Add another path tag within the same g tag. Give it the same dimensions as the heart in animateHeart2, but smaller.
    <animate
        id="animateHeart2"
        attributeName="d"
        from="M400 150 q-130 -60 0 90 q110 -150 0 -90 Z"
        to="M400 150 q-125 -60 0 100 q125 -150 0 -100 Z"
        fill="freeze"
        begin="animateHeart1.end"
        dur="1.5s"
        repeatCount="1"
    />
</path>

<path d="M400 155 q-110 -60 0 90 q110 -155 0 -90 Z">

</path>


Then animate it, to make it fade in. I like to call this one animateHeartHoleAppear, though it doesn't really matter what we call it since we won't be referencing it at all. The animation will begin after animateHeartContract ends. Unlike the pulsating heart, however, this one will be white in color.
    <animate
        id="animateHeart2"
        attributeName="d"
        from="M400 150 q-130 -60 0 90 q110 -150 0 -90 Z"
        to="M400 150 q-125 -60 0 100 q125 -150 0 -100 Z"
        fill="freeze"
        begin="animateHeart1.end"
        dur="1.5s"
        repeatCount="1"
    />
</path>

<path d="M400 155 q-110 -60 0 90 q110 -155 0 -90 Z">
    <animate
        id="animateHeartHoleAppear"
        attributeName="fill"
        from="rgba(255, 0, 0, 0)"
        to="rgba(255, 255, 255, 1)"
        fill="freeze"
        begin="animateHeartContract.end"
        dur="0.5s"
        repeatCount="1"
    />

</path>


Now tell me this ain't the most stylish damn thing you've ever seen!



And that's it! Happy Chinese New Year and happy Valentines Day!
Enjoy the results of your SVG animation, tinker with it, have fun! And have a lovey-dovey festive season!

May you have an ox-spicious Chinese New Year,
T___T

Monday 8 February 2021

Web Tutorial: CNY 2021 and Valentine's Day Animation (Part 1/2)

Happy Chinese Niu Year!

In case you didn't get the pun, Niu is the Chinese character for "Ox". It's the Year of the Ox, and quite coincidentally as it turns out, the festival coincides with Valentine's Day this year. It's rare that these two occasions collide, though they've come awfully close in recent years.

I thought long and hard about it, but eventually decided that the occasion deserves a two-in-one web tutorial. Today, we will create an SVG animation to commemorate both the Year of the Ox and Valentine's Day.

We begin with some HTML.
<!DOCTYPE html>
<html>
    <head>
        <title>CNY 2021</title>

        <style>

        </style>
    </head>
    
    <body>

    </body>
</html>


We'll also start with some CSS. These are the dimensions of the SVG.
<!DOCTYPE html>
<html>
    <head>
        <title>CNY 2021</title>

        <style>
            body
            {
                background-color: #FFFFFF;
                font-size: 10px;
            }

            #svgContainer
            {
                width: 800px;
                height: 550px;
                margin: 5% auto 0 auto;
                outline: 0px solid #FF0000;
            }

            svg
            {
                width: 800px;
                height: 550px;
                background-color: rgba(200, 200, 200, 0);
            }

        </style>
    </head>
    
    <body>

    </body>
</html>


Naturally, we'll then need a div with an id of svgContainer, and within it, the svg tag itself.
<body>
    <div id="svgContainer">
        <svg>

        </svg>
    </div>

</body>


Pre-emptively, we will now be adding more CSS classes - light, medium and dark. These will be applied to g tags, with fill properties for a light shade of pink, a nice orange, and brown.
svg
{
    width: 800px;
    height: 550px;
    background-color: rgba(200, 200, 200, 0);
}

g.light
{
    fill: rgba(255, 100, 0, 0.5);
}

g.medium
{
    fill: rgba(255, 100, 0, 1);
}

g.dark
{
    fill: rgba(100, 0, 0, 1);
}


Creating an ox head

The g tag is for grouping. It's not absolutely necessary in this context, but it helps to keep code well-organized. Let's begin with a g tag and style it using the CSS class medium. Here, we'll add two path tags. Remember that the SVG is 800 pixels wide and 550 pixels tall, so the starting coordinates for the d tag should give you a good idea where it begins. We have Z here because this is supposed to be a closed path.
<svg>
    <g class="medium">
        <path d="M400 320 Z"></path>
    </g>
</svg>


Here, q means we go 70 pixels left and 50 pixels down, then 70 pixels right again and 50 pixels up. This won't produce anything...
<svg>
    <g class="medium">
        <path d="M400 320 q -70 50 q 70 -50 Z"></path>
    </g>

</svg>


...until we add curves.
<svg>
    <g class="medium">
        <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    </g>
</svg>


Nice going.




We put another one in there, this one almost a mirror image, with the x-coordinates reversed.
<svg>
    <g class="medium">
        <path d="M400 320 q-70 -30 -70 50 q 0 -50 70 -50 Z"></path>
        <path d="M400 320 q70 -30 70 50 q 0 -50 -70 -50 Z"></path>
    </g>
</svg>


What this is supposed to do, is form the curve of the ox's scalp.



Next is two more paths that form the sides of the ox's face.
<g class="medium">
    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

</g>


It's early days yet, but can you see this taking shape?



Let's shape the brows!
<g class="medium">
    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

    <path d="M350 350 q20 0 40 20 Z"></path>
    <path d="M450 350 q-20 0 -40 20 Z"></path>

</g>


Yep, definitely a face forming.



Time to do the nose.
<g class="medium">
    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

    <path d="M350 350 q20 0 40 20 Z"></path>
    <path d="M450 350 q-20 0 -40 20 Z"></path>

    <path d="M380 430 q-50 30 20 50 q-30 -10 -20 -50 Z"></path>
    <path d="M420 430 q50 30 -20 50 q30 -10 20 -50 Z"></path>

</g>


The nose knows!



And finally, let's add the ears.
<g class="medium">                    
    <path d="M340 350 q0 50 -50 50 Z"></path>
    <path d="M460 350 q0 50 50 50 Z"></path>


    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

    <path d="M350 350 q20 0 40 20 Z"></path>
    <path d="M450 350 q-20 0 -40 20 Z"></path>

    <path d="M380 430 q-50 30 20 50 q-30 -10 -20 -50 Z"></path>
    <path d="M420 430 q50 30 -20 50 q30 -10 20 -50 Z"></path>
</g>


Damn, ain't this a beauty?



What we've done so far is add a basic frame for the face. We will next add some highlights. Add new g tag. Style it using the CSS class dark.
<svg>
    <g class="medium">                    
        <path d="M340 350 q0 50 -50 50 Z"></path>
        <path d="M460 350 q0 50 50 50 Z"></path>

        <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
        <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

        <path d="M340 350 q10 0 30 90 Z"></path>
        <path d="M460 350 q-10 0 -30 90 Z"></path>

        <path d="M350 350 q20 0 40 20 Z"></path>
        <path d="M450 350 q-20 0 -40 20 Z"></path>

        <path d="M380 430 q-50 30 20 50 q-30 -10 -20 -50 Z"></path>
        <path d="M420 430 q50 30 -20 50 q30 -10 20 -50 Z"></path>
    </g>

    <g class="dark">

    </g>

</svg>


Just a few strokes!
<g class="dark">
    <path d="M390 370 q-20 10 -30 -10 Z"></path>
    <path d="M410 370 q20 10 30 -10 Z"></path>

    <path d="M365 450 q10 -10 20 25 q0 -5 -20 -25 Z"></path>
    <path d="M435 450 q-10 -10 -20 25 q0 -5 20 -25 Z"></path>

    <path d="M340 355 q0 20 -20 30 Z"></path>
    <path d="M460 355 q0 20 20 30 Z"></path>

</g>


And what a difference that makes.



Just add a little shading here. Add a new g tag with a CSS class of light.
<g class="light">

</g>


<g class="medium">                    
    <path d="M340 350 q0 50 -50 50 Z"></path>
    <path d="M460 350 q0 50 50 50 Z"></path>

    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

    <path d="M350 350 q20 0 40 20 Z"></path>
    <path d="M450 350 q-20 0 -40 20 Z"></path>

    <path d="M380 430 q-50 30 20 50 q-30 -10 -20 -50 Z"></path>
    <path d="M420 430 q50 30 -20 50 q30 -10 20 -50 Z"></path>
</g>


Another two path tags here...
<g class="light">
    <path d="M400 330 q90 0 5 100 Z"></path>
    <path d="M400 330 q-90 0 -5 100 Z"></path>

</g>

<g class="medium">                    
    <path d="M340 350 q0 50 -50 50 Z"></path>
    <path d="M460 350 q0 50 50 50 Z"></path>

    <path d="M400 320 q-70 -30 -70 50 q0 -50 70 -50 Z"></path>
    <path d="M400 320 q70 -30 70 50 q0 -50 -70 -50 Z"></path>

    <path d="M340 350 q10 0 30 90 Z"></path>
    <path d="M460 350 q-10 0 -30 90 Z"></path>

    <path d="M350 350 q20 0 40 20 Z"></path>
    <path d="M450 350 q-20 0 -40 20 Z"></path>

    <path d="M380 430 q-50 30 20 50 q-30 -10 -20 -50 Z"></path>
    <path d="M420 430 q50 30 -20 50 q30 -10 20 -50 Z"></path>
</g>


...and this gives us a nice ox head! Well, except that this ox head needs horns.



Time to get horny! (heh heh) We add another g tag styled using the CSS class dark. This one has to render behind the head, so it has to be written first in the SVG.
<g class="dark">

</g>


<g class="light">
    <path d="M400 330 q90 0 5 100 Z"></path>
    <path d="M400 330 q-90 0 -5 100 Z"></path>
</g>


I won't go into detail how the path tags are shaped. The curve is going to be a little more extreme, though.
<g class="dark">
    <path d="M380 320 l-30 5 q-80 -110 40 -50 q-100 -40 0 45 Z"></path>
    <path d="M420 320 l30 5 q80 -110 -40 -50 q100 -40 0 45 Z"></path>

</g>

<g class="light">
    <path d="M400 330 q90 0 5 100 Z"></path>
    <path d="M400 330 q-90 0 -5 100 Z"></path>
</g>


Here's our ox head, complete with horns.



Some animation

Just for fun, let's animate the eyes a little. Make this guy wink. To to that, we need to insert an animate tag within the path tag that rendered one of the eyes. In this case, I choose the eye on my left.
<g class="dark">
    <path d="M390 370 q-20 10 -30 -10 Z">
        <animate />
    </path>
    <path d="M410 370 q20 10 30 -10 Z"></path>

    <path d="M365 450 q10 -10 20 25 q0 -5 -20 -25 Z"></path>
    <path d="M435 450 q-10 -10 -20 25 q0 -5 20 -25 Z"></path>

    <path d="M340 355 q0 20 -20 30 Z"></path>
    <path d="M460 355 q0 20 20 30 Z"></path>
</g>


Give this animation an id, animateEyeClose. The attribute we want to animate is d. The from attribute's value is exactly the same as the path's d attribute. For to, we will copy the same for now. We don't want the animation to revert abruptly, so set fill to "freeze". repeatCount is definitely 1, because we only want this animation to repeat on our terms. I intend to have this animation begin 5 seconds into loading, and take half a second to execute. So set begin and dur accordingly.
<path d="M390 370 q-20 10 -30 -10 Z">
    <animate
        id="animateEyeClose"
        attributeName="d"
        from="M390 370 q-20 10 -30 -10 Z"
        to="M390 370 q-20 10 -30 -10 Z"
        fill="freeze"
        begin="5s;
        dur="0.5s"
        repeatCount="1"

    />
</path>


Now adjust to.
<path d="M390 370 q-20 10 -30 -10 Z">
    <animate
        id="animateEyeClose"
        attributeName="d"
        from="M390 370 q-20 10 -30 -10 Z"
        to="M390 375 q-20 0 -30 -10 Z"
        fill="freeze"
        begin="5s;
        dur="0.5s"
        repeatCount="1"
    />
</path>


Blink and you'll miss it!



Add a second animate tag that reverses the first one. The id will be animateEyeOpen. from and to are the exact opposite of animateEyeClose's from and to values. For the begin attribute, set it to start when animateEyeClose ends.
<path d="M390 370 q-20 10 -30 -10 Z">
    <animate
        id="animateEyeClose"
        attributeName="d"
        from="M390 370 q-20 10 -30 -10 Z"
        to="M390 375 q-20 0 -30 -10 Z"
        fill="freeze"
        begin="5s"
        dur="0.5s"
        repeatCount="1"
    />

    <animate
        id="animateEyeOpen"
        attributeName="d"
        from="M390 375 q-20 0 -30 -10 Z"
        to="M390 370 q-20 10 -30 -10 Z"
        fill="freeze"
        begin="animateEyeClose.end"
        dur="0.5s"
        repeatCount="1"
    />

</path>


This will be fine if you only want the ox to wink once. But I want our ox to keep winking. So at the begin attribute of animateEyeClose, we specify that animateEyeClose not only begins after 5 seconds, it will also begin 10 seconds after animateEyeOpen ends. So now our ox should wink 5 seconds after the SVG loads, and every 10 seconds thereafter!
<path d="M390 370 q-20 10 -30 -10 Z">
    <animate
        id="animateEyeClose"
        attributeName="d"
        from="M390 370 q-20 10 -30 -10 Z"
        to="M390 375 q-20 0 -30 -10 Z"
        fill="freeze"
        begin="5s;animateEyeOpen.end + 10s"
        dur="0.5s"
        repeatCount="1"
    />

    <animate
        id="animateEyeOpen"
        attributeName="d"
        from="M390 375 q-20 0 -30 -10 Z"
        to="M390 370 q-20 10 -30 -10 Z"
        fill="freeze"
        begin="animateEyeClose.end"
        dur="0.5s"
        repeatCount="1"
    />
</path>


Next

You didn't think this was it, did you? There's more to our animation than the ox head. Stay tuned!

Wednesday 3 February 2021

The Big Tech Fallout From The Capitol Riots

It has been a crazy few weeks. Well, for the world at large, with the threat of COVID-19 and all, the entire year of 2020 and beyond has been nothing short of insane. But for the USA, things went a little extra nuts with that riot on the Capitol on the 6th of January.

I've avoided talking about it till now, because I firmly believe that as a Singaporean, it's not really my place to comment on USA politics. After all, I happen to find it extremely stupid and off-putting when non-citizens presume to tell Singaporeans what to think about Singapore's Government. Thus, I try not to do that. Until it begins affecting my industry. Then all bets are off.

The result of the fiasco where then-President Donald Trump was accused of inciting the mob to storm the Capitol, was that he was subsequently removed from Twitter and Facebook. Up to now, that "inciting" part is a bit of a mystery to me. I read a transcript of that speech and it just about put me to sleep.


Trump banned for
egging people on?

But anyway, back to Trump's removal from Social Media.

In related news, Social Media app Parler also had a rough time of it. Admittedly, I'd never heard of them until recently. Apparently, they were the destination and gathering point for all the extremist Right-wing groups, and their numbers surged when Twitter and Facebook began to purge those from their platform. Despite calls do do so, Parler refused to do likewise and purge these groups, citing Freedom of Speech or somesuch. However, this was soon brought to an end when Apple and Google removed the app from their respective platforms so that no one could install the app from those platforms. And then the nail in the proverbial coffin was introduced when Amazon, Parler's hosting provider at that time, withdrew their services citing Parler's refusal to remove content that incited violence.

In effect, both Donald Trump and the extreme faction of Right-wingers were pretty much cut off from Social Media. True, there was still e-mail and internet, but they no longer had a voice. Or, at least, they no longer had a platform from which they could spread their message.

Tech platforms taking sides?

It didn't take long for Left-wingers (and Trump-haters in general) to gloat that tech appeared to have finally chosen to side with them. Or their counterparts, the Right-wingers, to decry big tech for exactly the same thing. And indeed, it did appear that Twitter, Facebook, even Pinterest, had dropped all pretense of impartiality and done what they've been dying to do all along - embrace their Leftist base.


Taking sides.

I think these deluded children have forgotten one fact. These are tech businesses. They're not interested in your politics beyond what they can do to make money out of it. Nor should they ever be. Alienating roughly half your user base in order to pander to the dramatics of the other half, is a poor business decision.

There have been cries as to how tech is practicing censorship and stifling the oh-so-important Freedom of Speech. With all due respect, Freedom of Speech refers to Government action. These are private entities. As such, Twitter, Facebook and Amazon, among others, were perfectly within their rights - whether legally or ethically. Also, Donald Trump didn't leave Twitter and Facebook much choice in the matter here. Trump's speech, as insignificant as it may have been to me personally, undeniably meant something else to his base. And allowing him to keep stoking the fire in light of what happened at the Capitol, would have been highly irresponsible. So no, I don't think what they did was wrong. But it does raise some troubling questions.

Who to entrust with that kind of power?

Trevor Noah, one of my favorite comedians, has openly criticized big tech in the clip below. No, not for doing what they did, but for doing it too late.



What he said stood out to me, and as much as I enjoy watching him, it wasn't entirely in a good way.
"I think it's really funny how Social Media companies say they don't have a magic button to stop hate online, but then when Trump lost, suddenly they were like, oh we do have that button, here it is! So what - now these companies want a cookie for doing the right thing too late?"

On one hand, the dude has a point. Things should never have gotten as far as they did. And the fact that these companies seemingly waited till Donald Trump was soon to be no longer President, to act, does look an awful lot like cowardice.

On the other hand, what constitutes the right thing? Some might say - supporting causes that should be supported, and not tolerating causes that don't deserve your support. But who gets to decide that? These things are highly subjective. Do we really want Apple to decide which apps can or can't be online? Do we really want, say, Twitter, deciding what people can and can't read in public discourse? Sure, Trump-haters might be all for it now, but forget Trump. Look beyond petty grievances, and you'll realize how scary this is if big tech can be politicized to this extent.

Already, we have articles like these claiming that the Capitol riots should not be confused with the violence of Black Lives Matter or the HongKong protests that took up much of 2019. That's a matter of perspective, and highly subjective. What is an objective fact, is that laws were broken and public property was damaged, and lives were lost. Tech companies can, of course, be trusted to make decisions according to objective fact. It's when they are called upon to make subjective moral value judgements on behalf of their very diverse user base, that we might want to start worrying. What if Amazon one day decides that they don't want you to exist online? If you can't go to Amazon to host your app, where else could you go that has the same reach? If you can't build a Social Media presence on Facebook or Twitter, could you simply go elsewhere? Technically, you could, but options are slim to none.

Twitter
, Facebook, Amazon, et al, cannot and should not be faulted for having the huge user base they have today. But the fact remains that the recent banning of Trump and the deplatforming of Parler has laid bare the scary amount of power they do hold. It's a mind-boggling amount of power to have, and nobody wants that responsibility. Remember the case of Matthew Prince and The Daily Stormers roughly three years ago? Back then, Prince said something similar to what Jack Dorsey, CEO of Twitter, is saying today - no one should have that kind of power, and decisions like these set a dangerous precedent. Already, Twitter has the nigh-impossible task of enforcing its own rules consistently. Is it fair to expect them - or anyone, for that matter - to be the Moral Police? Sure, last week it was Donald Trump that got the short end of the stick. Who cares? He deserved it, right? But see... what happens when the big tech companies decide someone else deserves it? Someone you actually like? One day, it's the President of the USA, albeit an outgoing one. What's to stop these big tech companies from denying service to, say, Singapore? What if for some reason or other, Facebook suddenly decides that Singaporeans don't deserve Social Media?

Finally...

Some will tell you that these tech companies are private entities, and they can conduct business as they see fit. They aren't obliged to be consistent, or owe anyone any explanation regarding their decisions. And they certainly don't owe anyone a platform.

I'm not disputing any of that; it's all absolutely true.

But bear this in mind - business relationships are built on trust. If a company cannot be trusted to be transparent and act with some logical consistency, and instead is seen making arbitrary decisions based on internal or external pressures, that's bad for business. Something I suspect these companies are all too aware of.

Beware that slippery slope,
T___T