Draw the Moon

[Home]   [Puzzles & Projects]    [Delphi Techniques]   [Math topics]   [Library]   [Utilities]

 

Search

Search WWW

Search DelphiForFun.org

As of October, 2016, Embarcadero is offering a free release of Delphi (Delphi 10.1 Berlin Starter Edition ).     There are a few restrictions, but it is a welcome step toward making more programmers aware of the joys of Delphi.  They do say "Offer may be withdrawn at any time", so don't delay if you want to check it out.  Please use the feedback link to let me know if the link stops working.

 

Support DFF - Shop

 If you shop at Amazon anyway,  consider using this link. We receive a few cents from each purchase.   Thanks.


Support DFF - Donate

 If you benefit from the website,  in terms of knowledge, entertainment value, or something otherwise useful, consider making a donation via PayPal  to help defray the costs.  (No PayPal account necessary to donate via credit card.)  Transaction is secure.

Contact

Feedback:  Send an e-mail with your comments about this program (or anything else).

Search DelphiForFun.org only

 

 

Introduction

Drawing an image of phases of the moon is a good exercise in the use of the arc and floodfill functions of a TCanvas control.

We'll use a TPaintbox control to provide the canvas.  TCanvas's OnPaint exit will contain the actual drawing code.  The ellipse function requires  opposite corners be specified.  If the differences between the x corner coordinates and y corner coordinates are the same, a circle is produced - a good starting point for a full moon.  The fun part is eliminating portions of the circle to represent phases of the moon.

A Little Lunar Background

Moon phases exist because  we usually see only a portion of the side of the moon illuminated by the sun.  The moon rotates around the earth once a month (moonth?).  The plane of rotation is almost the same as the plane of the earth's rotation around the sun (5 degrees difference).  During the 2 weeks that the moon is inside of  our solar orbit, between us and the sun, we see less than half of the side of the moon that is facing us, a crescent moon.  When it's outside of our orbit, we see between half and all of the moon it's called a gibbous moon.   A  moon is said to be waxing during the period when the visible portion is increasing and  waning when the visible portion is decreasing.   They combine to produce the repeating "moon adjective" sequence: new , waxing crescent, first quarter,  waxing gibbous, full, waning gibbous, last quarter, waning crescent, and finally back to new describing a full cycle of phases of the moon.    

Looking down from the North star , the moon  rotates around the earth counter-clockwise, the same direction that we rotate around the sun.  This causes the illumination from the left during the waning phase, as it move around "in front" of us and from the right  as it waxes on around to the full moon position.  

Now we have enough information to produce our moon animation.  We'll pass 2 parameters, moonphase, a number between 0 and 1 representing the visible fraction of the moon's face, and waxing, a Boolean flag indicating whether  illumination is increasing or decreasing.   

The illusion

Imagine a circular disk suspended at eye level with the face vertical to the ground (I used a floor fan.)  Now walk around it and notice the apparent disk assumes an elliptical shape, collapsing to a vertical line and expanding again to an ellipse and  circle as you move around it.    Expand the disk to a ball and uses a bright light  for the disk and you'll see the same effect, except that the ball blocks your view of the back half of the disk, so you'll see half an ellipse moving across the face of the ball.  TCanvas has an Arc procedure that  will draw half ellipses and that's what we'll use.       

ARC and Floodfill

Arc takes 8 parameters representing 4 points.  Two opposite corners defining  an ellipse and 2 points representing endpoints of 2 lines from the center of the ellipse.  The arc is drawn counterclockwise around the perimeter of the ellipse from the point where the first line intersects it to the point of intersection with the second line.  Our starting and ending points will always be the top and bottom center of the moon image.    The tricky bit is determining which half of the ellipse to draw.  

Floodfill will fill in the dark part of the moon.  To use floodfill, we must specify a point inside of the area to be filled.  Mode fsborder will then fill outward in all directions until a specified color (the black border for us) is reached.   Near the edges can be a little troublesome.  For example, the moon may  be a pixel  smaller than the width specified.  So the point RightX-1, CenterY might be on the border, not inside.   The code has some tests to avoid these anomalies.       

 Here's a table I used to help sort out which side to draw and which side to fill with black.

Direction

WAXING

WANING

Phase 0.0 0.25 0.50 0.75. 1.00 0.75 0.50 0.25
Image
Draw Ellipse Side N/A Right N/A Left N/A Right N/A Left
Dark Side N/A Left Left Left N/A Right Right Right
Radius of Crescent Ellipse Rad 1/2 Rad 0 1/2 Rad Rad 1/2 Rad 0 !/2  Rad

        

Notice that there are two cases, phase 0.0 and phase 1.0, where waxing/waning doesn't matter.  In all other cases we'll darken the left side if waxing and right side if waning.  We'll also have to draw the ellipse arc from bottom to top  for the right side crescents by putting the bottom coordinate first in the call to Arc.  

Also notice that the radius of the ellipse defining the arc varies from the radius of the moon, Rad, to 0 and back to Rad as phase moves from the 0 to 1.  I experimentally determined the function as Delta=Rad*abs (Moonphase- 0.5), where abs is Delphi's absolute value function.

Moon Image

I added an  image of the real moon just to see how it would work.   You'll see a few pieces of code to handle it.  In FormActivate, we create and load a bitmap if the image file exists.  The image file was resized beforehand to closely match the TPaintBox size.  The border image of the image is cleaned up at load time by setting to black all pixels that are more than Rad pixels from the center.   I also discovered that TBitmap uses the Windows palette to realize colors.  As a result black is displayed as the darkest color defined in the current palette.  No real harm except that is the moon is set to true black with Floodfill, it becomes darker than the surrounding sky.  I found 2 solutions.  Setting the bitmap's PixelFormat property to pf24bit after the image is loaded will force a palette that recognizes true black.  In the second fix,  the one currently implemented, the paint routine uses the value of pixel[1,1] as black for border drawing and filling.  Variable myBlack contains true black if image is not being displayed  or the current palette representation if it is.              

With that background, I think the code will make some sense.   Have fun. 

Browse/Download Source 

bulletBrowse source code
bulletDownload source (includes moon image file)

Further Explorations

I noticed that TCanvas also has polybezier procedure that might be used in place of arc to draw the half ellipses.  That would allow adjustment for the apparent tilt of the crescent when to moon is high in the sky.   As far as I can determine, there is no ability to draw angled ellipses or arcs to accomplish this.   
If more realistic images were required, the shadow edge should gradually transition from black to transparent.  A lot of computation would probably be required to do this.  The effect represent the twilight zone if you were standing on the moon watching as the sun rose or set.   I believe this zone only exists during the time when part of the sun is visible - no atmosphere to extend the light zone as on earth.  
Another computationally intense enhancement would  merge the darkness with the background image to represent the earthshine which keeps the moon dimly visible even when in the sun's shadow. 
Floodfill is a time consuming operation - enough so that the animation speed slows noticeably when large areas are being filled.   The only simple solution I can think of is to vary the sleep time between frames, reducing the delay  for low phase cases.  Maybe something like (50+50*phase) milliseconds.          

 

 

 

 

     

 

  [Feedback]   [Newsletters (subscribe/view)] [About me]
Copyright 2000-2017, Gary Darby    All rights reserved.