Search

Geek Girl Joy

Artificial Intelligence, Simulations & Software

Tag

JavaScript

SVG Platformer

Can you go from art to program in one or two steps? Well, that’s what today’s post is about.

One of the cool things I remember about web development from years ago was Adobe Flash.

Before you boo me, hear me out!

I’m not saying Flash is a better technologically than HTML5, The Name of Your Favorite JavaScript Framework, CSS, Web Assembly etc… However one place Flash excelled was visual design & layout… an important part of the web!

The problem with many modern tools isn’t that they can’t convey design, it’s that they decouple the design and the development processes!

In practice this means writing code to describe the elements of your software like HTML and later writing more code to style the elements (like CSS, SASS or LESS), none of which is actually visual, though you definitely can get some great results!

Flash Builder (or whatever it was called) was half art studio and half IDE (Integrated Development Environment) where you could draw anything and it was an “object” and you could write code (ActionScript) to control it’s behavior. It wasn’t a mockup or illustration, it was the actual program!

As I recall, once the switch to ActionScript 3 was made the ability to store your code on the objects themselves was depreciated in favor of using references and listeners stored in the main keyframe timeline… I preferred keeping my code on the objects themselves but I digress.

Even with the change to where you stored your code you could still accomplish anything you wanted with the centralized keyframe code and some developers even found this easier to maintain than storing the code on the components.

You would setup your scripts on layered keyframe’s that extended to the last keyframe used in the project, or the last frame that needed that code and by using a sort of “goto keyframe name or id” method you could actually build complex applications quite easily, and more importantly… visually!

That’s why all the games used to be made with Flash, you could basically draw a picture and then turn it into an animation or even a full program in a couple of hours. This meant you were free to experiment & push boundaries.

Now, yes of course there are visual workflows you can use today.There are WYSIWYG editors and CMS App Platforms like WordPress, Drupal & Joomla not to mention the full featured layout capabilities of site builder tools like Wix.

Fundamentally though these tools facilitate laying out HTML elements and applying CSS and maybe some JavaScript via a drag and drop interface. Which is significantly faster than doing visual development via code in my opinion, though I am not arguing it is inherently “better”.

Unlike the aforementioned tools which specialize in “page based” HTML applications, Flash was an element or object that you embed into your page that used Vector Graphics to create lossless re-sizable images, animations and applications.

Inside the Flash movie/app you could draw anything and you were not constrained to HTML elements but you were also not required to code the visual elements.

This made for a wonderfully rapid prototyping experience that I was unable to reproduce until I tried working with Unity 3D which describes itself as “the ultimate game development platform” though I’d go so far as to describe it as “the ultimate app development platform”.

Think about it, at the time of writing this Unity supports 25 Platforms including Desktops (Win/Mac/Linux), the mobile OS’s, and all the major gaming consoles, not to mention the Smart TV’s, Watches etc…. Any platform you want your app on, including the web, well… Unity pretty much supports it right out of the box. Oh, and it’s free until you make $100K a year with it, not too shabby!

The catch? Well, its highly optimized but leans in the 3D gaming direction (though I’ve built 2D apps with Unity) so the applications it produces tend to have a larger size (as far as my tests go) than if you used PhoneGap/Cordova or went native. My guess is this is due to the embedded physics engine and graphics rendering code that gets packaged with the app but i’m only guessing, and there are a few options that let you exclude unnecessary things from the compiled app.

Then again, you may be able to make use of those features in your app so it need not be a negative either.

In any case, the problem as I see it with Unity is that the builder isn’t readily available on Linux, but it will build for Linux ❓ Maybe they should build Unity with Unity so that it can Unity being Unity… 😛

I am aware they kinda released a limited version for Linux… but I could never seem to make it work right and the truth is that it takes some decent (but not outrageous) resources to run the Unity builder application so most micro computers are out and sadly it won’t run on the ARMf architecture so using a Raspberry Pi to do Unity development is just not happening.

Is there another way?

Well, there is a modern Vector Graphic format available for the web called SVG that is basically XML code that can be written in a text editor or it can be drawn using a program like Inkscape (free and what I use) or Adobe Illustrator if you prefer a commercial paid tool.

Since SVG is code, if you place the code inside your HTML (sadly not link to or embed) you don’t just get a static vector image but instead you get elements that are accessible via the DOM (Document Object Model) that you can manipulate using CSS and JavaScript.

That last part should really interest you if you enjoy rapid application prototyping!

Which is the origin of this project, I wanted to know… could I rapid prototype an application by just drawing a picture and writing some code?

Understand that I am not talking about drawing a picture, slicing it then building an app from sliced components or using the sliced images as placeholders or writing code to draw the slices onto a canvas context.

I challenged myself to see if I could only write code that was part of the core functionality and not basic graphic asset creation and certainly not the code to display it, just manipulate it.

I set about creating a very simple “proof of concept” a while back that is basically a “Die Rolling App” that you can view a live example of here: SVG Roller though I never wrote about it. Roller is half image and half app but very basic.

Roller consists mainly of showing and hiding elements in the SVG based on a button click and a random number… good but not all that flashy!

Recently I have been wanting a better SVG application that would be more visual and expand on what I have already done but retain the simplicity of “Draw It then Code it”.

So, I opened up Inkscape and drew this image:


I grouped all the associated assets and gave them id’s like “cloud1”, “player”, “coin2” etc… then saved the image as demo.svg and closed Inkscape.

Why a game? Well, it’s more visual than my SVG Roller and I think it illustrates more of what is possible with an SVG app.

After that I opened demo.svg with a text editor and copied the SVG code into the body tag of my HTML file (remember you can’t link to it you have to include the code in the HTML).

I then wrote a little CSS that helps position the SVG on the page, applied a background color, disabled text highlighting and changed the cursor to the hand icon when the mouse is over a button, minimal CSS.

After that I wrote the JavaScript that turns the image into a playable application.

Game.js

Here is all the code that makes the SVG Platformer game demo work:

var keyEvents = {}; // keyboard state object

// Listen to keyboard outside of game loop to be less "blocky"
var onkeydown = onkeyup = function(key){
  key = key || event; // IE Fix 😦

  if(key.type == 'keydown'){
    keyEvents[key.keyCode] = true;
  }
  else{
    keyEvents[key.keyCode] = false; 
  }
  //console.log(keyEvents);
}


var game = document.getElementById('game'); // A reference to the SVG
if(game){
    game.addEventListener("load",function(){
    ///////////
    // Functions
        
    // Clear Instructions    
    // removes the instructions element
    function ClearInstructions() {
      instructions.remove();
    }

    // Get Position
    // This function gets the current (x,y) cordinates of GetPosition(object)     
    function GetPosition(object){
      var transformlist = object.transform.baseVal;
      var group = transformlist.getItem(0);
      var X = 0;
      var Y = 0;
      if (group.type == SVGTransform.SVG_TRANSFORM_TRANSLATE){
        X = group.matrix.e;
        Y = group.matrix.f;
      }
      return [X, Y];
    }
    
    // Collide
    // A basic box collision detector
    function Collide(element1, element2) {
      var collisionBox1 = element1.getBoundingClientRect();
      var collisionBox2 = element2.getBoundingClientRect();

      return !(collisionBox1.top > collisionBox2.bottom ||
        collisionBox1.right < collisionBox2.left ||
        collisionBox1.bottom < collisionBox2.top ||
        collisionBox1.left > collisionBox2.right);
    }

    // Inside
    // A basic inside box collision detector
    function Inside(element1, element2) {
      var collisionBox1 = element1.getBoundingClientRect();
      var collisionBox2 = element2.getBoundingClientRect();

      return (collisionBox1.top <= collisionBox2.bottom && 
        collisionBox1.bottom >= collisionBox2.top && 
        collisionBox1.left <= collisionBox2.right && 
        collisionBox1.right >= collisionBox2.left);
    }
      
    // Get Bank Total
    // Get the number of diamond or coins the player has
    function GetBankTotal(element){
      var currentValue = element.textContent;
      return parseInt(currentValue);
    }
    
    // Collect 
    // Increment the Coin or a Diamond "player bank"
    function Collect(element){
      element.textContent = GetBankTotal(element) + 1;
    }
      
    
            
    ///////////
    // Game play
    
    // Set the "constants"
    var step = 1;
    var jump = 20;
    var gravity = 1.5;

    // Setup references to the "named" SVG XML elements
    var gameOver = game.getElementById("gameover"); // A hidden "eater/detector" element below the play area to detect player death
    var instructions = game.getElementById('instructions');  // Instructions element
    var gameOverMenu = game.getElementById("gameovermenu");  // Game over screen element
    var player = game.getElementById("player");              // The player element
    var playerCoins = game.getElementById("playercoins");    // The "bank" element showing how many coins the player has collected
    var playerDiamonds = game.getElementById("playerdiamonds");// The "bank" element showing how many diamond the player has collected

    //  Setup references to the "named" SVG XML coin elements
    var coinPieces = ['coin1', 'coin2'];
    var coins = [];
    coinPieces.forEach(element => {
      coins.push(document.getElementById(element));
    });
        
    // Setup references to the "named" SVG XML diamond elements
    var diamondPieces = ['diamond1'];
    var diamond = [];
    diamondPieces.forEach(element => {
      diamond.push(document.getElementById(element));
    });

    // Setup references to the "named" SVG XML ground elements
    var terrainPieces = ['ground1', 'ground2', 'ground3', 'ground4'];
    var terrain = [];
    terrainPieces.forEach(element => {
      terrain.push(document.getElementById(element));
    });

    var winningBankTotal = diamondPieces.length + coinPieces.length;


    // Clear the instructions after 3 seconds
    setTimeout(ClearInstructions, 3000);


    // Redraw Game Loop
    var redrawRate = 30; // microseconds
    var gameLoop = setInterval(function(){
      fall = true; // always try to fall
      allowedToJump = false;// disallow jumping because player might be falling
      allowedToMove = true; // allow moving until the player is dead

      // Check for collisions with ground elements
      terrain.forEach(ground => {
        // If there is a collision with the ground
        if(Collide(player, ground)){
          fall = false; // Stop falling
          allowedToJump = true; // Allow jumping
        }
      });

      // if player fell below the ground
      if(Inside(player, gameOver)){
        fall = false; // stop falling
        allowedToJump = false; // dont allow jumping
        allowedToMove = false; // player is dead stop player movment
        ClearInstructions(); // just in case
        gameOverMenu.style.display = "inline"; // show game over menu
      }


      // if there was no collision between a ground element and
      // the player
      if(fall === true){
        position = GetPosition(player); // get updated player position
        allowedToJump = false; // dont allow jumping
        player.transform.baseVal.getItem(0).setTranslate(position[0], position[1] + gravity); // player falls
      }


      if(allowedToMove === true){
        position = GetPosition(player); // get updated player position
        // keyboard movment
        // left || a
        if (keyEvents[37] === true || keyEvents[65] === true) {
          if(position[0] > -10){
            player.transform.baseVal.getItem(0).setTranslate(position[0] - step, position[1]);
          }
        }
        // right || d
        if (keyEvents[39] === true || keyEvents[68] === true) {
          if(position[0] < 140){
            player.transform.baseVal.getItem(0).setTranslate(position[0] + step, position[1]);
          }
        }
        // up || w || space
        if ((keyEvents[38] === true || keyEvents[87] === true || keyEvents[32] === true) && allowedToJump === true) {
          player.transform.baseVal.getItem(0).setTranslate(position[0], position[1] - jump);    
        }
        // down || s
        if (keyEvents[40] === true || keyEvents[83] === true) {
          //console.log("Down");
        }
      }


      // Item Collection        
      // Collect coins
      coins.forEach(coin => {
        if(Inside(player, coin)){
          coin.remove();
          Collect(playerCoins);
        }
      });

      // Collect diamond
      diamond.forEach(diamond => {
        if(Inside(player, diamond)){
          diamond.remove();
          Collect(playerDiamonds);
        }
      }); 

      // Check for Win
      if((GetBankTotal(playerDiamonds) + GetBankTotal(playerCoins)) == winningBankTotal){
        clearInterval(gameLoop); // stop the game
        fall = false; // stop falling
        allowedToJump = false; // dont allow jumping
        allowedToMove = false; // player won stop player movment
        gameOverMenu.style.display = "inline"; // show game over menu
        // Change Game Over text to You Win
        game.getElementById("gameovermessage").textContent = '  You Win!';
      }

    }, redrawRate); // Game Loop - redraw every 30 microseconds
  }); // game load event listener
} // if game

 

As you can see from the code it supports WASD as well as the arrow keys and spacebar for movment. There is a win condition if you collect the two coins and the single diamond. You lose if you fall into one of the two spike pits.

Overall I am pleased with what I accomplished however the collision detection could be improved and there is quite a bit of room for improving how items are collected and enemies would be nice in addition to a larger/longer level, and maybe even a parallax scrolling  effect on some background elements as the player moves would also be nice, though again, I am happy with how it turned out.

You can Play a Live Demo: Here

You can Get the Code: Here

You can find a list of all my other posts on my Topics and Posts page.

I hope you enjoyed reading about and playing this SVG game prototype.

Your financial support allows me to dedicate time to developing projects like this and while I am publishing them without cost, that isn’t to say they are free. It takes me a lot of time and effort to build and publish projects like this for your enjoyment.

So I ask that if you like my content, please support me on Patreon for as little as $1 or more a month.

Feel free to suggest a project you would like to see built in the comments and if it sounds interesting it might just get built and featured here on my blog for you to enjoy.

 

 

Much Love,

~Joy

Advertisements

Button CSS Generator

Today we’re going to talk about a limited “generative system” that “writes” CSS code though I wouldn’t call this a ‘bot’.

What this generator does is “explore” the existing possibilities of a predefined problem space using known ranges and random selection. 😉

I wrote some of my thoughts regarding “generative systems” in relation to programmers and software developers in my article The Death of the Programmer and I also implemented a generative “writer bot” that I trained and used to write my article A Halloween Tale.

However in those cases I was mainly referring to semi-intelligent (in regard to the problem space) systems (“bots”) that are able to make choices on their own their own, and or are capable of learning, evolving or growing in some capacity based on input or state but that is not what we are doing today.

This is a simple generative tool (the Button CSS generator I provide below) is useful but it has no knowledge of what looks good (or bad) and no way to learn them either!

It can only provide you with options but it is far from replacing you.

You can view a Live Preview: Here 

Though in case you wanted it, here’s a screenshot:

There are only 3 files used to create the Button CSS generator: style.css, functions.js & index.html

Style.css

This is the CSS styles used by the index.html file. You will notice that there is a base button CSS template that all the buttons share when the page is first loaded.

The “hardcoded” styles are: text-align,  text-decorationdisplay, padding, font-size.

Once the page is loaded however I use JavaScript to generate and apply additional CSS attributes which are responsible for the variations in each button that you see.

The style.css file is an external style sheet and is linked to using <link rel=”stylesheet” type=”text/css” href=”style.css”> in the HTML file.

 

h1{
    width:100%;
    text-align: center;
}
    
button {
    /* hardcoded styles*/
    text-align: center;
    text-decoration: none;
    display: inline-block;
    padding: 15px 32px;
    font-size: 1.5em;
}

table{
    text-align: center;
    width:100%;
}

caption{
    font-size:2em;
}

#randomize{
    background-color: rgb(62, 176, 239); 
    color: rgb(138, 219, 215);
    border-color: rgb(28, 65, 39);
    border-width: 11px;
    border-style: dotted;
}

#show-css-wrapper{
    width: 100%;
    text-align: center;
    margin-left: auto;
    margin-right: auto;
}

#show-css-title{
    width: 100%;
    text-align: center;
    margin-left: auto;
    margin-right: auto;
    background-color: #999999;
}

#show-css{
    width: 40%;
    min-width: 420px;
    text-align: left;
    margin-left: auto;
    margin-right: auto;
    background-color: #eeeeee;
}

textarea{
    width: 100%;
    height: 200px
}

 

Functions.js

This file contains all the functions that the generator will use to make new button CSS styles.

There is some inline JavaScript in the HTML file that makes use of these functions and I will discuss that in the Index.html section below.

There is a lot of similarity between most of the functions and although much of their functionality can be wrapped up into one function I intentionally implemented the prototype generator this way.

I could say the reason is that I wanted to avoid early optimization but the real reason is that this way is just easier to read for someone who didn’t write the software and or is just learning to code.

Though in truth SetBackgroundColor(element) or SetBorderWidth(element) is not significantly more readable than a more optimized generic function like SetCSS(element, cssProperty,  optional [ value ])  and if I was going to spend more time developing this system I would probably end up going that route because the generic function would be far easier to maintain in the long run simply because it would be one function to maintain instead of many.

The functions.js file is an external script and is linked to the HTML file using the “src” (source) attribute on a script tag.

////////////////////////////////////
// Random Button CSS Generator Functions

// Will return 0 or 1
function CoinFlip(){
    return Math.floor(Math.random() * 2); //0/1
}
    

// Return a random string of RGB values e.g. "255, 255, 255"
function RandomRGBColor(){
    var r, g, b;
    
    r = Math.floor(Math.random() * 256);
    g = Math.floor(Math.random() * 256);
    b = Math.floor(Math.random() * 256);
    
    color = r.toString() + ', ' + g.toString() + ', ' + b.toString();

    return color;
}

// Set SetBackgroundColor()
function SetBackgroundColor(element, color = null){
    // No color given so set to random
    if(color === null){
        element.style.backgroundColor = 'rgb(' + RandomRGBColor() + ')';
    }else{ // Set to provided color
        element.style.backgroundColor = 'rgb(' + color + ')';
    }
}

// Set SetFontColor()
function SetFontColor(element, color = null){
    // No color given so set to random
    if(color === null){
        element.style.color = 'rgb(' + RandomRGBColor() + ')';
    }else{ // Set to provided color
        element.style.color = 'rgb(' + color + ')';
    }
}

// Set SetBorderColor()
function SetBorderColor(element, color = null){
    // No color given so set to random
    if(color === null){
        element.style.borderColor = 'rgb(' + RandomRGBColor() + ')';
    }else{ // Set to provided color
        element.style.borderColor = 'rgb(' + color + ')';
    }
}

// Set SetBorderWidth()
function SetBorderWidth(element, width = null){
    // No width given so set to random
    if(width === null){
        element.style.borderWidth = Math.floor(Math.random() * 14).toString() +'px';
    }else{ // Set to provided color
        element.style.borderWidth = width.toString() + 'px';
    }
}

// Set SetBorderStyle()
function SetBorderStyle(element, style = null){
    // No width given so set to random
    if(style === null){
        
        var styles = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'initial', 'inherit'];
        
        element.style.borderStyle = styles[Math.floor(Math.random()*styles.length)];
            
    }else{ // Set to provided color
        element.style.borderStyle = style;
    }
}

// Get the CSS for the button that was clicked and show it on the page
function ShowCSS(element){
    var style = window.getComputedStyle(element);
    var backgroundColor = style.getPropertyValue('background-color');
    var color = style.getPropertyValue('color');
    var borderColor = style.getPropertyValue('border-color');
    var borderWidth = style.getPropertyValue('border-width');
    var borderStyle = style.getPropertyValue('border-style');
    var padding = style.getPropertyValue('padding');
    var textDecoration = style.getPropertyValue('text-decoration');
    var display = style.getPropertyValue('display');
    var fontSize = style.getPropertyValue('font-size');
    var css = '';
    css += 'button{\n';
    css += '    background-color: ' + backgroundColor.toString() + ';\n';
    css += '    color: ' + color.toString() + ';\n';
    css += '    border-color: ' + borderColor.toString() + ';\n';
    css += '    border-width: ' + borderWidth.toString() + ';\n';
    css += '    border-style: ' + borderStyle.toString() + ';\n';
    css += '    padding: ' + padding.toString() + ';\n';
    css += '    text-decoration: ' + textDecoration.toString() + ';\n';
    css += '    display: ' + display.toString() + ';\n';
    css += '    font-size: ' + fontSize.toString() + ';\n';
    css += '}\n';

    document.getElementById('css-styles').innerHTML = css;
}

Index.html

Index.html is the core that brings all the pieces of this software together!

You will notice that I used divisional elements (div tags) and a textarea to create the section at the top of the page where the CSS code is shown when you click one of the buttons.

Beneath that is the randomize button hyperlinked to nothing, followed by a table. Why a table and not a responsive grid? Eh… it was faster for the prototype mainly.

Inside each cell of the the table is a button that has an onclick attrabute that passes the element’s ID reference ‘this’ to the function ShowCSS().

Beyond this the only thing of consequence in the HTML file is the inline JavaScript that uses the code from the functions.js script to make everything work.

When the page loads I use an array of strings of element id’s (the buttons) to locate & establish references to the DOM objects for each button and store those references in the buttons array.

Then, for each of the element references in the buttons array I set a background color as well as a font color randomly. After that I “flip a coin” to decide if a boarder should be applied to the button. If yes, I apply a random boarder color, width and style.

Once the code is done running all the buttons will be randomly styled and are ready for you to click them to get the CSS if you like them or click Randomize to get a new set of random buttons.

<html>
<head>
  <title>Random Button</title>
  <link rel="stylesheet" type="text/css" href="style.css"> 
</head>
<body>
  <!-- Title -->
  <h1>Random Button CSS Generator</h1>
     
    <!-- Show CSS -->
    <div id="#show-css-wrapper">
        <div id="show-css">
            <h2 id="show-css-title">Button CSS</h2>
            <p>Select a button to view it's CSS styles</p>
            <textarea id="css-styles">CSS will show here</textarea>        
        </div>
    </div>

    <!-- Table -->
    <table> 
      <tr>
          <td></td>
          <td>
              <h3>
                  <a href="">
                      <button id="randomize">
                          Randomize
                      </button>
                  </a>
              </h3>
          </td>
          <td></td>
      </tr>
         <tr>
          <td>&nbsp;</td>
          <td>&nbsp;</td>
          <td>&nbsp;</td>
      </tr>
      <tr>
        <td><button id="button-1" onclick="ShowCSS(this)">Button 1</button></td>
        <td><button id="button-2" onclick="ShowCSS(this)">Button 2</button></td>
        <td><button id="button-3" onclick="ShowCSS(this)">Button 3</button></td>
      </tr>
      <tr>
        <td><button id="button-4" onclick="ShowCSS(this)">Button 4</button></td>...
        <td><button id="button-5" onclick="ShowCSS(this)">Button 5</button></td>
        <td><button id="button-6" onclick="ShowCSS(this)">Button 6</button></td>
      </tr>
        <tr>
        <td><button id="button-7" onclick="ShowCSS(this)">Button 7</button></td>
        <td><button id="button-8" onclick="ShowCSS(this)">Button 8</button></td>
        <td><button id="button-9" onclick="ShowCSS(this)">Button 9</button></td>
      </tr>
    </table>

 
    <!-- Include the Functions -->
    <script src="functions.js"></script>    
    <script>
    // Button Element ID's
    var elementsIDs = ['button-1', 'button-2', 'button-3', 'button-4', 'button-5', 'button-6', 'button-7', 'button-8', 'button-9'];

    var buttons = []; // Array to Store Button References

    // Setup Element References to the Buttons
    elementsIDs.forEach(element => {
      buttons.push(document.getElementById(element));
    });

    // Apply Random CSS to each Button
    buttons.forEach(element => {
      SetBackgroundColor(element); // Set Initial Background Colors
      SetFontColor(element);       // Set Initial Font Colors

      if(CoinFlip() == 1){ // Has border?
        SetBorderColor(element);
        SetBorderWidth(element);
        SetBorderStyle(element);
      }
       
    });

    </script>
</body>
</html>

 

Where to go from here

As a proof of concept I think I accomplished my goal of creating a functional Button CSS Generator however a more complete prototype would want to include features like allowing you to edit the CSS of the selected button and have the changes reflect on the button in real time or via an “update” button.

Also, perhaps the ability to “favorite” or save a button’s CSS style so that you can reload it again later or some kind of “history” feature would be nice so that you can keep the button styles you like.

Additionally, the CSS attributes the generator uses are hard coded and the property or attribute “min/max” ranges are set as well… A nice feature would be be to have the option to edit/add or remove the CSS fields included in the generator and modify the ranges as well.

It would also be nice for the prototype to allow for the generation of vertical and horizontal menus using the button CSS and include the ability to edit the links or “onclick” actions and the text of the buttons.

The “ultimate” implementation of a generator like this would allow you to generate styles for all or some subset of the HTML elements not just buttons.

I will leave all these features for you to add to your own implementations at this time however if you like this project I may just add some or all of these features in the future.

You can view a Live Preview: Here

You can get the code on GitHub: Here

You can find a list of all my other posts on my Topics and Posts page.

I hope you enjoyed reading this article.

Your financial support allows me to dedicate time to developing projects like this and while I am publishing them without cost, that isn’t to say they are free. I am doing this all by myself and it takes me a lot of time and effort to build and publish these projects for your enjoyment.

So I ask that if you like my content, please support me on Patreon for as little as $1 or more a month.

Feel free to suggest a project you would like to see built in the comments and if it sounds interesting it might just get built and featured here on my blog for you to enjoy.

 

 

Much Love,

~Joy

 

Why We Marry Programming Languages

For right or wrong, better or worse, in sickness and in health… it happens to most of us at one point or another, we fall in love and I’m not talking that “puppy love” either…

I mean the “in it for the long term, I hear wedding bells” kind of love! 😛

When you have been programming for a goodly number of years and “gone steady” with at least a few languages, you find that generally there is a lot of overlap and similarity between them! Of course, there are differences… some specialize in their selected niche either by design or by homogenization of a developer community.

Essentially though, all programming languages are more or less capable of doing the same things, though they go about it in different ways.

I know you embedded developers are probably disagreeing with me at this point, let me just say I totally agree that when it comes to low level embedded situations, C Language is what you really want, so don’t bite my head off in the comments! 😛

My accedence (it’s a real word) about embedded applications notwithstanding, I think you can GENERALLY agree with this quote (though I’m not sure who to attribute the quote to) and I’m only just paraphrasing anyway, it goes like this:

“You can do just about the same things in all programming languages. What sets them apart is what they make easy and what they make hard.”

~Unknown

And that’s really the crux of it isn’t it!

After you have become familiar with your “lenguaje del amor” it’s suddenly like… traversing that billion-element 3D tuple isn’t so bad! 😛

Of course, it’s not quite that simple in reality but the fact that any given language can make something subjectively “easier” to an individual compared to another language due to a methodology or strategy employed by that language is very much at the heart of why programmers seem to “merry” their languages!

As I mentioned, there are various styles, methodologies and strategies implemented by the designers of programming languages that change how you think about problems they set out to tackle.

Further, despite comparing “multi-paradigm” languages with one another you get certain languages that just seem to fit better at solving certain types of problems.

I’ll give you an example of the subjectivity I’m talking about with 3 different “multi-paradigm” capable languages namely Python, JavaScript and my personal favorite PHP (some of you just gagged, please compose yourself… I’ll wait 😛 )…

So, lets use a string in each language as the subject of our examination.

In Python and JavaScript strings are said to be “immutable” whereas in PHP strings are “mutable”, now before I begin I am NOT stating that one is objectively better than the other, but I am going to try to demonstrate that there are differences between languages, which in and of itself should not be controversial.

Each has its pros and cons (I.E. reasons why an individual programmer may prefer one over the other), but they are all capable languages and great to know!

I simply aim to show that there ARE differences and those stylistic differences are a large factor, though of course not the only factor.

Python

Let’s start with “immutable” strings in Python. Let’s say we have a string variable with the label ‘name’ and either mistakenly assign or “point it to” the value of ‘coy’ or as part of our project we simply need the ability to modify the string.

Let’s then give ourselves the challenge of correcting the string during program execution to ‘Joy’ but without simply reassigning the name variable to ‘Joy’, in other words let change the ‘c’ to a ‘J’.

Now since Python strings are “immutable” they cannot be directly accessed and changed however that doesn’t mean that you CAN’T change them.

>>> name = 'coy'
>>> name[0] = 'J'
>>> print(name)

If you ran this code you would get this lovely little error:


Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'str' object does not support item assignment
name[0]

HONESTLY… I don’t think I have ever seen as CLEAN and pleasantly descriptive error in my entire tech career! Extreme kudos to the Python community! 🙂 That level of clarity is rare in programming errors!

In order to accomplish what we want we can do something like the following:

>>> name = 'coy'
>>> name = name[:0] + 'J' + name[1:]
>>> print(name)

The traditional “pythonic” response to this problem (I think) is to create a list and store the individual characters so that you can then get index access like this:

>>> name = list("Joy")
>>> name[0] = 'c'
>>> print(''.join(name))

Not better or worse than the other languages necessarily though not my personal favorite.

JavaScript

If you run this code you will NOT get an error…

var name = 'coy';
name[0] = 'J';
console.log(name);

Though the name variable remains ‘coy’! In order to accomplish what we want we can do something like the following:

var name = 'coy'; 
name = 'J' + name.substr(1);
console.log(name);

I don’t think there is a “prescribed” solution in the JS community though it is possible to do the Python solution and “split” the string into an array:

var name = 'coy'.split('');
name[0] = 'J';
console.log(name.join(''));

The thing is if you try this code on JSBin (not a sponsor) you get this rather cryptic error:

"error"
"@null.js:16:38
l</b.render/<@https://static.jsbin.com/js/prod/runner-4.1.0.min.js:1:13848
k</a.use/<@https://static.jsbin.com/js/prod/runner-4.1.0.min.js:1:10792
"

But it works fine on JSFiddle (not a sponsor) so I am not sure if it’s a versioning issue but coder beware! If there are Any JS coders in the audience feel free to contact me with the reason why this might be and I’ll credit you for the info! 🙂

PHP

PHP is a little different in that all strings are “byte” arrays already which just means that the indexes are already open to direct modification and that works like this:

$name = 'coy';
$name[0] = 'J';
echo $name;

The real difference being that with “immutable” strings you cannot change the value directly and instead have to reassign the value to another variable.

In both the JavaScript and Python examples we used the same label (name) for the variables but the internal ID’s for those variables are different. Using this methodology basically means having to “split” the string by hand, cull the unwanted value, insert the new value and recombine the string.

Again, neither approach is is objectively “correct” but if you work with a lot of string data you might soon come to appreciate the ease of PHP in that respect, I certainly do! 😛

The funny thing is though that although you can “marry” your language, it might not excel in all ways and you are then forced to admit that only one language cannot be everything you need it to be all the time… In fact, in my article The Impending Death of the Programmer I briefly talked about the need for “transpiler” software which does level the “playing field” somewhat but not entirely.

Several interesting tranpilers in the PHP community are wxPHP (not a sponsor) that allows for native WIN, MAC, & LINUX application development using PHP.

There is also KikAppTools (not a sponsor) which allows native Android & iOS mobile application development using PHP.

These tools extend the reach of a language significantly!

I am still waiting for Unity3D to implement a PHP version. 😛

Still though, it’s hard for just one language to “be all things to all people”, so to continue the metaphor, you just might find that “polyamory”… er “polyglotism” is the way to go! 😛

In any case, even if we are comfortable working in multiple languages… there will always our favorites!

Please Fund, Subscribe, Like, Share & Comment:

This post was made possible by the generous contributions of my sponsors on Patreon & anonymous contributions to my Bitcoin Tip Jar.

 

 

I will see you all in my next post!

Much Love,

~Joy

Ancestor Simulations Adding Elements & Solar Nebulae

It’s Friday so that means your probably planning to pick up a 6..er 24 pack of beer and holding up in front of your computer all weekend and reviewing and modifying all my recent changes to the solar systems generator prototype. Frankly I can’t say that I blame you because there is quite a bit to go over! 😛

Before we proceed I want to welcome and thank directly Erich Weidtmann over on Patreon for becoming my supporter! With your help I will be able to continue to bring you these great posts! The funds you contribute will go towards the fund to improve our hosting and computational capacity!

I have already added him over on my Sponsors page as a public and on going thank you for continuing to sponsor my posts and projects. If you would also like to add your name to my sponsors list then support me over on Patreon for as little as $12 a year!

 

Solar Nebulae

I know in my last post Monday Mini Makeup I didn’t think we were going to add Solar Nebulae  due to the large number of computations required (SO MANY) which results in a simulation that takes hours or days to complete. I did however say that I would attempt it and get back to you and well, I am pleased to say that it bugged me enough that I went ahead and added it anyway!

I conducted my first long simulation (15,000 epochs) after adding solar nebula and took 19 hours (roughly 1 epoch (cycle or frame of the simulation) every 13 Minutes & 12 Seconds) to complete the run and it had over 100 thousand orbital bodies ranging in size from ~marble or “ball bearing” size up to several hundred kilometers in diameter.

Before we look at the code let’s talk about what a solar Nebula is and look at some pretty pictures while we’re at it! 😛

Protoplanetary-diskAn artist’s concept of a protoplanetary disk, where particles of dust and grit collide and accrete forming planets or asteroids

 

SolarnebulaShock waves through icy parts of the solar nebula may be the mechanism that enriched ancient meteorites (called chondrites) with water — water that some believe provided an otherwise dry Earth with oceans.

Basically a solar nebula (protoplanetary disk) is all the stuff (rocks, minerals and elements) that make up a solar system just spread out and not congealed into large bodies like planets and moons because the system is still “young” and forming.

In practice this means that the main difference between the nebula and the protoplanets is size and by extension due to the additional mass and volume a larger chemical element diversity.

In our simulation, the orbital bodies that comprise the solar nebula are much smaller than protoplanets and we also don’t compute their orbits (movement). Further still, we do not enable collision detection from the perspective of the nebula.What this means is that although the objects will accrete if a planet passes close enough to them, they do not accrete with each other, which sucks but since they are not moving anyway this significantly reduces the computation required per frame but at the cost of a stationary cloud that would in reality orbit the star like the protoplanets.

I feel like this is a fair tradeoff however as it still enables the planets to grow and this even approximates the planets “clearing their neighborhood” as can be seen in this frame from one of the simulation I recently ran. Notice the empty areas that look like striations in the laminar flow of the orbits through the nebula.

Here is the code that creates the nebula, notice 3 things.

  1. The sizes are listed in both metric and imperial for your convenience! 😉
  2. The code is not much different from the code that creates the planets. We do this separately for simplicity however we could wrap this up into a single step if and when we clean up the code (remember this is a prototype).
  3. The code that positions the cloud objects is the same logarithmic spiral algorithm that we implemented when we were creating galaxies.

 if(isset($_POST['enable-solar-nebula'])){ 

	$a = 0.9759 + RandomFloat(0);
	$b = 0.1759;
	$steps = 7; 
	$radius = 0.8 * pi()* $steps;
	$max_spread = 0.7;
	$lower_disk_radius = -6;
	$higher_disk_radius = 6;
    
	
	for($i = 0; $i < 200000; $i++){

		// Nebula
		
		// Debris size
		$size = mt_rand(0,6);
		if($size == 0){
			$size = "0.0" . mt_rand(1,9); // 31.855 Kilometers - 286.695 Kilometers in Diameter
		}                                 // 19.7937793 Miles - 178.144014 Miles in Diameter
		
		elseif($size == 1){
			$size = "0.00" . mt_rand(1,9); // 3.1855 Kilometers - 28.6695 Kilometers in Diameter
		}                                  // 1.97937793 Miles - 17.8144014 Miles in Diameter
		
		elseif($size == 2){ 
			$size = "0.000" . mt_rand(1,9); // 318.55 Meters - 2.86695 Kilometers in Diameter
		}                                   // 348.370516208 Yards - 1.78144014 Miles in Diameter
		
		elseif($size == 3){
			$size = "0.0000" . mt_rand(1,9); // 31.855 Meters - 286.695 Meters in Diameter
		}                                    // 34.837051619 Yards - 313.53346457 Yards in Diameter
		
		elseif($size == 4){
			$size = "0.00000" . mt_rand(1,9); // 3.1855 Meters - 28.6695 Meters in Diameter
		}                                     // 3.48370516185 Yards - 31.3533464567 Yards in Diameter
		
		elseif($size == 5){
			$size = "0.000000" . mt_rand(1,9); // 0.00031855 - 0.00286695 Kilometers in Diameter
		}                                      // 1.04511155 Feet - 9.406003937008 Feet in Diameter

		elseif($size == 6){
			$size = "0.0000000" . mt_rand(1,9); // 3.1855 Centimeters - 28.6695 Centimeters in Diameter
		}                                       // 1.25413386 Inches - 11.28720472 Inches in Diameter
		

		
		
		
		  $angle = $radius * RandomFloat(3); // Pick a random point in the Spiral
		  $row = $a * exp($b * $angle) * cos($angle);
		  $row = $row + ($max_spread * $row * RandomFloat(0)) - ($max_spread * $row * RandomFloat(0));
		  $col = $a * exp($b * $angle) * sin($angle); 
		  $col = $col + ($max_spread * $col * RandomFloat(0)) - ($max_spread * $col * RandomFloat(0));

		  // Flip a coin to determine which arm 
		  // the star should be on.
		  if (mt_rand(0, 1) == 1){
			  // if heads put it on the second arm
			  // by inverting the values
			  $row = ($row/-1);
			  $col = ($col/-1);
		  } 

		  // Normalize positions to be within the image bounds
		  $row = MinMax($row, $lower_disk_radius, $higher_disk_radius, $img_size);
		  $col = MinMax($col, $lower_disk_radius, $higher_disk_radius, $img_size);

			
		$color = imagecolorallocate($system, mt_rand(10, 50), mt_rand(10, 50), mt_rand(10, 50));
		@imagefilledellipse ( $system, round($row), round($col), (float)$size, (float)$size, $color);
		
		
		array_push($planets, array(
	   'au'=>$row+$col,
	   'size'=>$size,
	   'color'=>$color,
	   'vy'=>0,
	   'vx'=>0,
	   'x'=>$col,
	   'y'=>$row,
	   'r'=>$radius,
	   'a'=>$angle,
	   'ax'=>0,
	   'ay'=>0,
	   'row'=>$row,
	   'col'=>$col,
	   'plot'=>true,
	   'motion'=>false
	   ));

	}
 }

I wish we had the computation capacity available to include motion on the nebula as well but we really have hit a brick wall in this regard. I may attempt to improve this by using the few remaining tricks up my sleeves but I highly doubt I will get enough improvement what would allow the sim to speed up enough for modeling the orbital dynamics of the nebula, but I’ll try and see what comes out the other side. 😛

Elements

Now lets talk about our burgeoning chemical system for a minute. It gets a few things right and a lot wrong or rather it simply omits quite a bit of whats makes elements, “elements” (namely subatomic particles).

The main thing to keep in mind is that it’s still an early implementation and treats all volumes of each element equally. Obviously this is nonsensical and I intend to improve this by accounting for the size of the orbital bodies so that we get a more accurate simulation.

Currently what is occurring is we track the correct distribution, transfer and spread of the elements but not their correct volumes. I intend to fix this issue and discus elements in more detail in my next post (I wasn’t actually planing to be implementing the solar nebula) so although a basic implementation is in place it requires a lot of work before we can consider the elements even remotely “done”. Having said that here is the code that initializes the elements:

 

if(isset($_POST['enable-elemental-simulation'])){
	////
	// Meet the elements
	////
	$chemical_elements = array('Hydrogen', 'Helium', 'Lithium', 'Beryllium', 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine', 'Neon', 'Sodium', 'Magnesium', 'Aluminium', 'Silicon', 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium', 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium', 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper', 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium', 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium', 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium', 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium', 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon', 'Caesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium', 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium', 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium', 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten', 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold', 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium', 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium', 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium', 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium', 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium', 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium', 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium', 'Livermorium', 'Tennessine', 'Oganesson');

	// initialize unique element spawn probability "weights" for this solar system
	$element_weights = array();
	$number_of_elements = count($chemical_elements); // count the elements
	for($i = 0; $i < $number_of_elements; $i++){
		array_push($element_weights, mt_rand(0, $number_of_elements));
	}
	// combine the element names as keys and the weights as values
	$chemical_elements = array_combine($chemical_elements, $element_weights);
}

 

This code generates the elements for a protoplanet based on the probability distribution (weights) for this unique solar system (not all systems will have the same amount of elements):



$body_elements = array();
	if(isset($_POST['enable-elemental-simulation']) && $_POST['enable-elemental-simulation'] == true){
		// Add Elements
		$number_of_elements_to_spawn = mt_rand(10,15);
		for($elem = 0; $elem < $number_of_elements_to_spawn; $elem++){

			//select an element
			$select_element = mt_rand(1,array_sum($chemical_elements));

			// Find the selected element
			$num = 0;
			foreach($chemical_elements as $value=>$weight){
				if(($num += $weight) >= $select_element){
					//echo "$weight $value" . PHP_EOL;
					
					// push element onto the orbital body
					array_push($body_elements, $value);
					break;
				}
			}
		}
	}

And that’s it for today!

You can find the updated code for this project over on my GitHub profile.

Please Like, Comment below & Share this post with your friends and followers on social media.

This post was made possible by the generous contributions of my sponsors on Patreon.

As always, have a great weekend and I will see you all in my next post!

Much Love,

~Joy

 

Ancestor Simulations Generating New Solar Systems Monday Mini Makeup

Happy Monday!

MY LOGIC IT FEELS GOOD TO BE BACK POSTING! Did you miss me? I know it has been only one week but I missed me! 😛

The truth is that as much as I keep making these projects and posts for you guys, I REALLY need this! The act of sitting down and thinking up not just a programming project every week, but also something that is intelligent (and hopefully entertaining) has been extremely cathartic for me!

You see, as much as your job may be difficult (it probably is) and boring at times (likely) and as much as your coworkers, clients, boss or employees can be a pain (It’s Monday so I know you are thinking it), all of those people are rational and you have every right to your frustrations!

I share no such luxury my friend because my boss is an irrational 7 month old baby who only loosely knows the words “baba” and “momma” !

Don’t misunderstand me, I certainly love my little Xavier, it’s just that unlike your adult employees, bosses, coworkers, clients, G/F, B/F, spouse, lover, spouse’s lover… what have you 😛 whose thoughts and motivations you can basically understand with a reasonable amount of effort and discussion, no matter how many times I carefully explain the dangers of wantonly crawling off the edge of the bed head first onto a hardwood floor he still takes every opportunity to try!

Further still, no matter how many times I explain how it hurts momma when he imagines himself as Tarzan and attempts to use my hair as vines to swing on and yet alas… he is just not amenable to the idea!

Regardless of the countless times I elaborate on why wiggling around when I am changing his “poopy” diapers creates “yucky messes” for momma to clean up, it is none the less… ineffectual!

The jarring truth is that you simply cannot reason with a 7 month old baby! Odds are at the very least, the people you deal with every day are leaps and bounds (many orders of magnitude) more acquiescent to compromise than Xavier is!

As such, I have confidence that you will get through this week with aplomb! 😉

Now, let’s talk about our Ancestor Simulation, shall we?

When you last saw this project Generating New Solar Systems I was getting ready for a “Friday Fun Fix” and I was contemplating the following three modifications to the Solar Systems Generator:

  • Adding loss of mass  to thermal energy release and heat transfer when there are impacts of orbital bodies.
  • Improved agglomeration of smaller particulates into larger bodies.
  • Adding chemical properties and transfer those chemicals during impacts.

First let me say that I feel like accounting for mass lost as thermal radiation released during orbital body impacts is an absolutely wonderful idea! However, at this point it is seemingly a bit more complex than we currently have the resources to model. If you think about it, we would have to keep track of subdivisions of masses and implement a much more complex particle simulation (not to mention model all the subatomic particles) and then extend that system (additional computation) to every frame (epoch) of the simulation… while this is a wildly fun idea, I don’t really feel like we would get all that much out of adding this at this juncture. So, I have decided that we are going to shelve this concept for the time being and perhaps at a later date, once we have upgraded our computational capacity we may revisit this.

In regard to improving agglomeration and instantiating our solar system from smaller particulates into larger bodies. We had implement collision detection and our planets would eat each other, quite vigorously in fact! However, more often than not we would end up with few stable orbits (one or two) but usually no sustained orbits. After a bunch of minor tweaks late this past week and weekend throughout the generator I have managed to get it to a state of routinely generating complex multi-body orbital patterns and I tweeted a teaser GIF on Friday:

Many of the code “tweaks” involved modifying the initialization of the orbital velocities and distance from the center star. What the planets currently do is basically “find their orbits” starting with an orbit larger than their velocity can sustain so their orbits decay from their initial position to an inner orbit closer to the sun. Once their velocity is sufficient to maintain the bodies distance from the sun it continues to orbit until it collides with something or the simulation ends.

I am now quite pleased with the behavior of the simulation in regard to agglomeration at this point however I really wish we had the ability to extend this further and implement something akin to an accretion disk from which the orbital bodies form from clouds of material rather than instantiating small but fully formed planetoid’s which then proceed to ‘consume’ one another in order to grow. This too would require an improved particle system and at this time I don’t have enough ‘big iron‘ to make this a reality. Again though, I am really pleased with what I have accomplished already!

Further, I am toying with the idea of imposing a second “grid” overlay on the solar system that would contain a probability distribution of “virtual particles‘ (perhaps using a modified version of the plasma fractal which we already implemented over in my post on Generating Planet Terrain) that could be used to organically “grow” planets without the need to simulate each and every atom and collision directly.

In this case we would say that as an orbital body passes through each position in the probability distribution, based on the density of “virtual particles” the centaurs would grow by some amount based on that position’s material abundance and at the same time reduce the available particulate in the cloud (probability distribution) thereby actually accreting using only probability and not simulating the individual interactions of the collisions.

If I have the opportunity I will toy with this concept a little and see if it is worth implementing in full.

Which brings us to the final enhancement under consideration, implementing a chemical or elemental system. Let me first say that this is HAPPENING for sure! I have the initial element system partially implemented already and if all goes well I expect to unveil this feature in this upcoming “Friday Fun Fix”. Additionally, while we need not limit ourselves to real elements (nothing says we can’t just make up a new periodic table for our simulated universe) we will however be using the same chemical elements as are available in our universe. 🙂

In any case here is a newly rendered solar system containing 19 stable orbital bodies.

Click or tap the animation to view it full size.

 

You can find the updated code for this project over on my GitHub profile.

Please Like, Comment below & Share this post with your friends and followers on social media.

Please support me over on Patreon for as little as $1 a month.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

As always, have a great week and I will see you all in my next post!

Much Love,

~Joy

Ancestor Simulations Generating New Solar Systems

Happy labor day everyone! While the rest of your family is getting ready to barbecue, putting drinks on ice and otherwise getting ready to celebrate the holiday… your exceptionally high IQ’d self is waiting for a post from little o’l me… as it should be! 😛

Why do we celebrate ‘labor day’ by not working anyway? I mean we’re literally ‘celebrating labor’ right? But then how do you not do the very thing you are celebrating on the day you celebrate it? Hmmm :-/ Bazaar don’t you think?

So after last weeks posts:

The natural next step to creating new solar systems would seem to be using another generator as we had in my Ancestor Series posts… and that is in fact the case.

After more than few minutes of hacking away I stepped back from my keyboard to admire the frankencode proto solar system simulation I had assembled and then it occurred to me, there is just no way I can host this for everyone. The raw computation alone is… ‘hefty’ to say the least but then to generate the animations as well… its just more processing capacity than I have available on my web server.

So I will publish some of the code below for you to read and the full project over on my GitHub profile. You are welcome to obtain a copy and play with the code yourself and I encourage you to do so! 🙂

Here’s a very early render of my results, notice that while planets do orbit, they pass right through each other and the center star as well!

 

A great first attempt but certainly not ready for our ‘ancestors’ to live in! Most of the planets are flung out of the system and the others all seem to plunge into the star at some point, not stable enough to safely call this place home!

I then proceeded to implement collision detection on the orbital bodies with each other as well as with the sun! Now things are starting to take shape!

Currently the planets will grow when they ‘eat’ (impact) one another however 100% is merged and there is 0 lost as thermal energy so I might address that if it bugs me enough! 😛

I was also thinking about Astronaut Don Petti’s experiment that I wrote about over in my post on Ancestor Simulations Generating Galaxies, where he demonstrated the role in which microgravity caused the initial formation of the so called ‘agglomerates’ from which larger and larger orbital bodies would form until you end up with an active solar system and stuff smashing into other stuff all the time!

Well, if possible I would also like to see if we can’t simulate that by starting our system with maybe hundreds or thousands of really small particles and having them grow into larger orbital bodies that become planets.

If so, perhaps we might also be able to assign each initial asteroid or comet a set of chemical properties and transfer those chemicals during impacts. We might even use that data to get some kind of read out regarding the available chemicals on each orbital body, if I have time I think it would really add a nice layer of information that our ‘ancestors’ can use to decide on where to go  if they ever become space faring! Further, we might use it to decide where it is even possible for life to evolve let alone colonize if it’s not their home planet.

Here is the Code

set_time_limit(3000); // Adjust as needed


function MinMax($value, $min, $max, $size){
    return abs(($value - $min) / ($max - $min) * $size);
}


function RandomFloat($big){
    return mt_rand(0, $big) . "." . mt_rand(0,99);
}

// Get the file extension 
function GetFileExtension($image){
    $path_data = pathinfo('images/' . $image);
    return strtolower($path_data['extension']);
}

// Load an image resource
function LoadImage($image){
    $ext = GetFileExtension($image);
    if($ext == 'jpg' || $ext == 'jpeg'){$image = imageCreateFromJpeg('images/' . $image);}
    elseif($ext == 'png'){$image = imageCreateFromPng('images/' . $image);}
    elseif($ext == 'bmp'){$image = imageCreateFromBmp('images/' . $image);}
    else{return null;}
    return $image;
}

// Buffer Images
function EncodeImage($image){
    
    global $images, $buffered;
    ob_start(); // Start the buffer
    imagegif($image); // Output image buffer as a gif encoded still
    $images[]=ob_get_contents(); // add the gif still to the images array
    //$buffered[]=$delay; // Delay in the animation.
    $buffered[]= (1); // Delay in the animation (ms * delay in seconds)
    ob_end_clean(); // Stop the buffer
}
 
// Create Gif
function CreateGif($num_images){
    global $images, $buffered;
    
    // Do something with each image
    for($i = 1; $i < $num_images; $i++)
    {
        $image = LoadImage("$i.png");
        EncodeImage($image); // Buffer image
        imagedestroy($image); // Free memory
    }
    // Generate the animation
    $gif = new GIFEncoder($images,$buffered,0,0,0,0,0,'bin');
    
    
    // Save the gif
    $animation_file = fopen('orbit.gif', 'w');
    fwrite($animation_file, $gif->GetAnimation());
    fclose($animation_file);
}


$delta_time = 0.1; // 0.1 // 0.03
if(isset($_POST["epocs"])){$simulations = $_POST["epocs"];}
else{$simulations = 210;}


if(isset($_POST["zoom"])){$high = $_POST["zoom"];}
else{$high = 6;}
$low = $high * -1;


////
// Create Solar System
////
if(isset($_POST["exponent"])){$exponent = $_POST["exponent"];}
else{$exponent = 9;}

$img_size = pow(2, $exponent) + 1;

if(isset($_POST["number-of-bodies"])){$number_of_orbital_bodies = $_POST["number-of-bodies"];}
else{$number_of_orbital_bodies = 9;}

$system = imagecreatetruecolor($img_size, $img_size);
$space_black = imagecolorallocate($system, 0, 0, 0);


////
// Define Sun
////
$sun_size=mt_rand(8,30); // Size
$sun_x = $img_size/2; // center the sun on x
$sun_y = $img_size/2; // center the sun on y
$sun_yellow = imagecolorallocate($system, 255, 255, 0); // Color
@imagefilledellipse ( $system, $sun_y, $sun_x, $sun_size, $sun_size, $sun_yellow);


$planets = array();
// generate system
for($i = 0; $i < $number_of_orbital_bodies; $i++){

    ////
    // Define Planets
    ////
    $au_from_sun=RandomFloat(2+$i); // AU from Sun
    $size=RandomFloat(8); // Size
    $color = imagecolorallocate($system, mt_rand(50, 255), mt_rand(50, 255), mt_rand(50, 255)); // Color
    $vy = RandomFloat(1); // Velocity
    $vx = 0;              // Velocity    
    $x = $au_from_sun; // Initial x position
    $y = $au_from_sun; // Initial y position


    // randomly flip spawn quadrant
    if(mt_rand(0,1) == 1){
        $y = $y / -1;
    }
    if(mt_rand(0,1) == 1){
        $x = $x / -1;
    }
    
    // randomly shift to half
    if(mt_rand(0,1) == 1){
        // position is in 
        if($y + $y/2 > $img_size){
            $y -= $y/2;
        }else{
            $y += $y/2;
        }
    }
    if(mt_rand(0,1) == 1){
        // position is in 
        if($x + $x/2 > $img_size){
            $x -= $x/2;
        }else{
            $x += $x/2;
        }
    }

    $r = sqrt(pow($x,2) + pow($y,2)); // Orbital radius at this position
    $a =  ($au_from_sun * 0.00002) / pow($r, 2);
    $ax = -$a * $x / $r; // Divide the force for the angle between x & y
    $ay = -$a * $y / $r; // Divide the force for the angle between x & y
    // Normalize positions to be within the image bounds
    $row = round(MinMax($y, $low, $high, $img_size));
    $col = round(MinMax($x, $low, $high, $img_size));
        
    // plot planet
    @imagefilledellipse ( $system, $row, $col, $size, $size, $color);
    
    array_push($planets, array(
       'au'=>$au_from_sun,
       'size'=>$size,
       'color'=>$color,
       'vy'=>$vy,
       'vx'=>$vx,
       'x'=>$x,
       'y'=>$y,
       'r'=>$r,
       'a'=>$a,
       'ax'=>$ax,
       'ay'=>$ay,
       'row'=>$row,
       'col'=>$col,
       'plot'=>true));
}


// Output image
imagepng($system, "images/0.png");
// free memory
imagedestroy($system);


for($i = 0; $i < $simulations; $i++){
    ////
    // Solar System
    ////
    $system = imagecreatetruecolor($img_size, $img_size);
    
    ////
    // Sun
    ////
    // Plot Sun
    @imagefilledellipse ( $system, $sun_y, $sun_x, $sun_size, $sun_size, $sun_yellow );
    
    foreach($planets as $key=>&$planet){
        if($planet['plot'] == true){
            ////
            // Planet
            ////
            $planet['vx'] = $planet['vx'] + $planet['ax'] * $delta_time; // New velocity
            $planet['vy'] = $planet['vy'] + $planet['ay'] * $delta_time; // New velocity
            $planet['x'] = $planet['x'] + $planet['vx'] * $delta_time; // New position
            $planet['y'] = $planet['y'] + $planet['vy'] * $delta_time; // New position
            $planet['r'] = sqrt(pow($planet['x'],2) + pow($planet['y'],2)); // Orbital radius at this position
            $planet['a'] =  $planet['au'] / pow($planet['r'], 2); // Acceleration / angle
            $planet['ax'] = -$planet['a'] * $planet['x'] / $planet['r']; // Divide the force for the angle between x & y
            $planet['ay'] = -$planet['a'] * $planet['y'] / $planet['r']; // Divide the force for the angle between x & y
            // Normalize positions to be within the image bounds
            $planet['row'] = MinMax($planet['y'], $low, $high, $img_size);
            $planet['col'] = MinMax($planet['x'], $low, $high, $img_size);
            // Plot Planet
            @imagefilledellipse ($system, $planet['row'], $planet['col'], $planet['size'], $planet['size'], $planet['color']);
            
            // planet went out of bounds
            /*
             _____
           .|     |.
            |  *  |
            |_____|
                .  */
            if($planet['row'] <= 0 + ($planet['size']/2) + 5 || $planet['col'] <= 0 + ($planet['size']/2) + 5 || $planet['row'] >= $img_size - ($planet['size']/2) + 5 || $planet['col'] >= $img_size - ($planet['size']/2) + 5){
                $planet['color'] = $space_black;
                $planet['plot'] = false;
            }
            // if planet got too close to sun
            // as defind by the the center point of the sun
			// sourrounded by a box of:
			// the radius of the sun + n pixels
            /*
             _______
            |       |
            |  [*]  |
            |_______|
            */
            if( ($planet['row'] >= $sun_x - ($sun_size/2)+5) && ($planet['row'] <= $sun_x + ($sun_size/2)+5) && ($planet['col'] >= $sun_y - ($sun_size/2)+ 5) && ($planet['col'] <= $sun_y + ($sun_size/2)+5))
            {
                $planet['color'] = $space_black;
                $planet['plot'] = false;
            }
            
            
            // orbital body other than the sun collision handeling
            /*
             ________
            |        |
            |  >..<  |
            |________|
            */
            // for all the planets 
            foreach($planets as $key2=>&$planet2){
                if($planet2['plot'] == true){
                    if($key != $key2){ // that are not this planet
                        
                        // deturmine the larger of the two
                        if($planet['size'] > $planet2['size']){ // planet is bigger than planet 2
                            
                            // then check for collisions
                            if(($planet2['row'] >= $planet['row'] - ($planet['size']/2)+5) && ($planet2['row'] <= $planet['row'] + ($planet['size']/2)+5)
                              && ($planet2['col'] >= $planet['col'] - ($planet['size']/2) + 5) && ($planet2['col'] <= $planet['col'] + ($planet['size']/2)+5))
                            {
                                // remove smaller planet
                                $planet2['color'] = $space_black;
                                $planet2['plot'] = false;
                                
                                // add the mass of the
                                // small orbital body to
                                // the larger orbital body
                                $planet['size'] = $planet['size'] + $planet2['size'];
								
								// Planet may not be larger than our arbitrarily sized sun
                                if ($planet['size'] > $sun_size){$planet['size'] = $sun_size; }

                            }
                        }else{// planet2 is bigger than planet 1
                            
                            // then check for collisions
                            if(($planet['row'] >= $planet2['row'] - ($planet2['size']/2)+5) && ($planet['row'] <= $planet2['row'] + ($planet2['size']/2)+5)
                              && ($planet['col'] >= $planet2['col'] - ($planet2['size']/2) + 5) && ($planet['col'] <= $planet2['col'] + ($planet2['size']/2)+5))
                            {
                                // remove smaller planet
                                $planet['color'] = $space_black;
                                $planet['plot'] = false;
                                
                                
                                // add the mass of the
                                // small orbital body to
                                // the larger orbital body
                                $planet2['size'] = $planet2['size'] + $planet['size'];
                                
								// Planet may not be larger than our arbitrarily sized sun
                                if ($planet2['size'] > $sun_size){$planet2['size'] = $sun_size; }
                            }
                        }
                    }
                }
            }
        }
    }
    
    // Output Solar System
    imagepng($system, "images/" . ($i + 1) . ".png");
    // Free Memory
    imagedestroy($system);
}

include('GIFEncoder.class.php'); // GIFEncoder class
$images;
$buffered;
@CreateGif($simulations);

 

Here is an example of the output of the generator as it is currently, click or tap the animation to view it full size. It might take a second or two for it to load. More preview animations are available over on GitHub!

If you would rather I had this project running right here on this page, consider supporting me over on Patreon. One of the many things I would like to do with the funding my patrons give me is upgrade my servers!

Please Like, Comment below & Share this post with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

With that, I’m going to go eat a hot dog!

Have a great week and I will see you all in my next post!

Much Love,

~Joy

Ancestor Simulations Generating Galaxies Friday Fun Fix

Happy Friday everyone!  Checking in with another Friday Fun Fix.

The hardest challenge I have faced this week has been lack of sleep… well, most weeks actually. 😛

Xavier has been in this pattern where he has been ‘cat napping‘ his way through the night and day rather than nice consistent longer sleep cycles. Consequently I have found it difficult to obtain sufficient sleep myself. I literally passed out for an hour on Wednesday and didn’t even realize I had slept! Xavier was playing in his crib and didn’t even notice.

When I woke up… I noticed the time and realized I had “lost” an hour of productivity to sleep. Most days I’m tired but Tuesday Night / Wednesday morning left me completely exhausted!

Thankfully Xavier let me get some rest Wednesday evening and while even now I’m still tired, looking at the glowing LCD screen in front of me… let alone reading math notation and programming doesn’t just flat out hurt. All those years of late night “crunch programming” sessions to finish a feature are finally paying off as preparation for motherhood! 😛

In anycase, I didn’t have a ton of time to work on adding anything to the Galaxy generator however I did refactor the code a little to simplify redundancy.

Further, adding additional features to the Galaxy generator doesn’t seem all that necessary at this time and I am happy with where it is currently and feel comfortable moving forward on Monday. That isn’t to say that I don’t have plenty of ideas on how to improve and extend it, just that we will implement them when they are necessary.

My code refactoring eliminated 16 lines of redundant code taking the Galaxy generator from 304 lines of code down to 288.

You may view a live version of this project here if you would like and access the source for this project on GitHub here.

Here  is one more sample image for your enjoyment. This MASSIVE image is 8192x8192px and contains ~ 1/44,444th the number of stars in our actual galaxy (the Milky Way). Click the image below to open it and then zoom in… really makes you ponder how vast our universe really is when you consider that despite using a huge image and filling it with as many stars as I reasonably could, it still pales in comparison to the real thing!

Please Like, Comment below & Share this post with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Ancestor Simulations Generating Galaxies

Welcome back to another installment of my Ancestor Simulations series, previous posts in this series series are:

Carl Sagan famously wrote these two excerpts in his book Cosmos:

“If you wish to make an apple pie from scratch, you must first invent the universe.” & “The nitrogen in our DNA, the calcium in our teeth, the iron in our blood, the carbon in our apple pies were made in the interiors of collapsing stars. We are made of star stuff.”

Fundamentally what Mr. Sagan is talking about is that we are “here and now” in ‘spacetime‘ and that everything you see around you, from the seemingly mundane soil beneath your feet to the silicon chips in the device you’re using to read this post right down to even the very constituents of your own body all originate not here on earth, but out there among the stars!

Our Sun (Sol) is ~4.6 billion years old and when our solar system formed, it did so by accreting and coalescing material already present in our galaxy. It turns out that microgravity is an incredibly potent force for for creating emergent larger structures (like galaxies and solar systems) from small clumps of available materials. Those materials came from somewhere outside our solar system (we are the remnants of other long dead solar systems) to form or become our solar system (the Sol system).

Below I have linked to a a video of Astronaut Don Pettit PhD and Science Officer for Expedition 6 of the ISS explaining an experiment he conducted that illustrates how microgravity would have operated in our early solar system to take “small particles that are bouncing around colliding with each other and coagulating into larger agglomerates”.

Those “small particles” Mr. Pettit is talking about in his experiment were salt (NaCl) and is analogous to the ‘Interstellar Medium‘  which is ultimately the byproduct of the life cycles of stars that existed and “died” before our system had even formed.

In time (~5 Billion years from now) our sun is expected to expand into a ‘Red Giant‘ at which point it is believed that it will consume our little Earth and anything still around on the planet. If Sol remains intact and isn’t itself consumed by a black hole or by some other celestial body, it will progress through it’s life cycle to become a ‘White Dwarf‘. Trillions of years later Sol may eventually become a ‘Black Dwarf‘ star after scattering it’s heavy elements into interstellar space as a ‘Planetary Nebula‘ thereby sowing the seeds (that ‘star stuff’ Mr. Sagan wrote about) of countless other worlds, some of which may eventually harbor life just like our solar system does now. Some of that ‘star stuff’, will be the very same particles that are part of your body at this very moment!

The Milky Way‘ (our home galaxy) is a vast area spanning ~100,000 light years in diameter and and ~2000 light years thick. It is estimated that the Milky Way Galaxy is ~13.7 Billion years old, making it almost as old as the universe itself!

Which now brings us to this week’s project, Generating Galaxies. The method we will use to create galaxies today is called a Logarithmic Spiral and it will allow us to create all the main distinct types of galaxy: Spiral, Quasar, Globular & Irregular.

So without further hemming and hawing, here is our code:



set_time_limit(300); // 5 Minutes adjust as needed
ini_set('memory_limit', '3G'); // 3 GB Adjust as needed

function RandomFloat(){
    return '0.'. mt_rand(0,9999);
}

function MinMax($value, $min, $max, $size){
    return abs(($value - $min) / ($max - $min) * $size);
}

function vignette($im){
    $width = imagesx($im);
    $height = imagesy($im);

    for($row = 0; $row < $width; ++$row){
        for($col = 0; $col < imagesy($im); ++$col){  
            $index = imagecolorat($im, $row, $col);
            $rgb = imagecolorsforindex($im, $index);
            $sharp = 1; // 0 - 10 small is sharpnes, 
            $level = 0.7; // 0 - 1 small is brighter    
            $l = sin((pi() + RandomFloat()) / $width * $row) * sin((pi() +  RandomFloat()) / $height * $col);
            $l = pow($l, $sharp); 
            $l = 1 - $level * (1.2 - $l);
            $rgb['red'] *= $l;
            $rgb['green'] *= $l;
            $rgb['blue'] *= $l;
            $color = imagecolorallocate($im, $rgb['red'], $rgb['green'], $rgb['blue']);
            imagesetpixel($im, $row, $col, $color);  
        }
    }
    return(true);
}

if(isset($_POST["galaxy-type"])){$galaxy_type = $_POST["galaxy-type"];}
else{$galaxy_type = 'spiral';}


if(isset($_POST["exponent"])){$exponent = $_POST["exponent"];}
else{$exponent = 9;}

$size = pow(2, $exponent);

if(isset($_POST["apply-gaussian"])){$apply_gaussian = $_POST["apply-gaussian"];}
else{$apply_gaussian = false;}

if(isset($_POST["apply-colorize"])){$apply_colorize = $_POST["apply-colorize"];}
else{$apply_colorize = false;}

if(isset($_POST["red"])){$red_number = $_POST["red"];}
else{$red_number = mt_rand(0,255);}

if(isset($_POST["green"])){$green_number = $_POST["green"];}
else{$green_number = mt_rand(0,255);}

if(isset($_POST["blue"])){$blue_number = $_POST["blue"];}
else{$blue_number = mt_rand(0,255);}

if(isset($_POST["apply-greyscale"])){$apply_greyscale = $_POST["apply-greyscale"];}
else{$apply_greyscale = false;}

if(isset($_POST["apply-negate"])){$apply_negate = $_POST["apply-negate"];}
else{$apply_negate = false;}

if(isset($_POST["apply-vignette"])){$apply_vignette = $_POST["apply-vignette"];}
else{$apply_vignette = false;}


if(isset($_POST["number-of-stars"])){$number_of_stars = $_POST["number-of-stars"];}
else{$number_of_stars = 300000;}

$a = 0.9759 + RandomFloat();
$b = 0.1759; // ~nautilus shell
$steps = 5; 
$radius = 3 * pi()* $steps;
$max_spread = 0.3;
$min_star_position = -76458.735624534;
$max_star_position = 102693.535433 ;

// Image Resource
$galaxy = imagecreatetruecolor($size, $size);

// Colors
$white = imagecolorallocate($galaxy, 255, 255, 255);
$yellow = imagecolorallocate($galaxy, 255,255,0);
$red = imagecolorallocate($galaxy, 255,0,0);
$blue = imagecolorallocate($galaxy, 0,0,255);
$black = imagecolorallocate($galaxy, 0, 0, 0);
$dark_grey = imagecolorallocate($galaxy, 50, 50, 50);

// Create Image of Galaxy
for($i = 0; $i  $number_of_stars * 0.6){
          $radius = 6 * pi()* $steps; // increase radius and spread out
      }
   }
   elseif($galaxy_type == 'quasar'){// Quasar Galaxy
      if($a != 1){
          $a = mt_rand(1, 2) + RandomFloat();
          $b = 0.2;
          $max_spread = RandomFloat();
      }
      
      $row = $a * exp($b * $angle) * cosh($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sinh($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
  elseif($galaxy_type == 'globular'){// Globular/Elliptical Galaxy
      if($max_spread != 1){     
          $a = mt_rand(1, 2) + RandomFloat();
          $b = 0.2;      
          $max_spread = 1;
          $radius = 6 * pi()* $steps;
      }
      
      $row = $a * exp($b * $angle) * cos($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sin($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
  elseif($galaxy_type == 'irregular'){// Irregular Galaxy
      if($max_spread != 1){     
          $a = 8 + RandomFloat();
          $b = 0.2;      
          $max_spread = 1.3;
          $radius = 6 * pi()* $steps;
      }
      
      $row = $a * exp($b * $angle) * cos($angle);
      $row = $row + ($max_spread * $row * RandomFloat()) - ($max_spread * $row * RandomFloat());
      $col = $a * exp($b * $angle) * sin($angle); 
      $col = $col + ($max_spread * $col * RandomFloat()) - ($max_spread * $col * RandomFloat());
  }
    
  // Flip a coin to determine which arm 
  // the star should be on.
  if (mt_rand(0, 1) == 1){
      // if heads put it on the second arm
      // by inverting the values
      $row = ($row/-1);
      $col = ($col/-1);
  } 

  // Normalize positions to be within the image bounds
  $row = MinMax($row, $min_star_position, $max_star_position, $size);
  $col = MinMax($col, $min_star_position, $max_star_position, $size);

  
  $colors = array($white, $white, $white, $dark_grey, $yellow, $red, $blue);
  @imagesetpixel($galaxy,round($row),round($col), $colors[mt_rand(0,6)]); // Plot the star position
}

if($apply_gaussian == true){
    imagefilter($galaxy, IMG_FILTER_GAUSSIAN_BLUR); // blur
}
if($apply_colorize == true){
    @imagefilter($galaxy, IMG_FILTER_COLORIZE, $red_number, $green_number, $blue_number); // Shift colors
}
if($apply_greyscale == true){
    @imagefilter($galaxy,  IMG_FILTER_GRAYSCALE); // grey scale
}
if($apply_negate == true){
    @imagefilter($galaxy, IMG_FILTER_NEGATE); // inverse colors
}
if($apply_vignette == true){
    @vignette($galaxy); // vignette
}


// Output image
imagepng($galaxy, "galaxy.png");

// free memory
imagedestroy($galaxy);

You may view a live version of this project here if you would like and access the source for this project on GitHub here.

You can see the results of this code in the images below.

Click for Full Size Images

Spiral

Globular

Quasar

Irregular

That’s it for today ladies and gentlemen.

Please Like, Comment below & Share this post with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Ancestor Simulations Generating Planet Terrain Friday Fun Fix

Surprise Friday post everybody! 😋

First, relax. Yes you will get another post on Monday, I’m working on the next part of our Ancestor Simulation and I think I have something you will really enjoy but in the mean time I wanted to do a second post on Generating Planet Terrain.

Why a second post? Well, to be honest… I know the week long wait for more projects is tough, so I’m trying to produce more content for you guys! I tend to prefer to focus on creating quality projects and code and frankly that takes time, but I know some of you really need your fix. So, I’m experimenting with the idea of “documenting the process” as a way of letting you guys “peer behind the curtain” so to speak.

I can’t guarantee I will  always do a second post or that I will only do two posts. I’m merely committing to produce more content on the whole and this post is part of that.

Anyway, I hope you have been enjoying the Ancestor Simulation series, I have received an overwhelming positive response from my readers about this topic and that might strike you as quite odd when you consider the fact that my content is quite “dry” and frequently full of long complex code but I think that speaks to the idea that if you make your content interesting enough your ‘target audience’  (the smart techies who read my posts) will keep coming back, and for that… I am very grateful! 🙂

So on to Ancestor Simulations. No code today, or rather if you want to get the updated code you can get a copy of this project over on GitHub for free. While you are there be sure to follow me and star the repo so I know you like where the project is going. Speaking of which… just how far am I willing to go with this project? Well that depends on you guys, keep letting me know you are interested and feel free to suggest anything you want to see our sim do and I will see if I can’t accommodate 😉 however ultimately I am implementing this for myself because I find it interesting, I hope you do as well.

This past Monday I posted the Generating Planet Terrain post and before we move forward I wanted to add a few additional features and changes:

  1. More color variation possibilities via an additional ‘Color Variance’ pass over the terrain data and more manual access to the coloring system. It’s still based on a ‘Terrain Color Palette’ however this second pass allows MANY more color combinations which ultimately makes each planet feel more unique and special.
  2. Added a Generate Random button.
  3. Slight change to the water system.
  4. Slight change to the ice system.
  5. Added a ‘Display Type’ setting so you can now view the map as a ‘Map’ (as before) or as a ‘Planet’ (see below).

You can of course preview a running version here.

And with that I hope you all have a great weekend and I will see you Monday.

Click for Full Size Images

Please Like,  Share my posts with your friends and followers on social media.

If you would like to suggest a topic or project for an upcoming post feel free to contact me.

If you found this article useful or want to help me grow consider supporting me over on Patreon.

With that, have a great week & I will see you all in the next post!

Much Love,

~Joy

Blog at WordPress.com.

Up ↑

%d bloggers like this: