reptile7's JavaScript blog Monday, February 29, 2016

Things of Shapes, Side A
Blog Entry #361

Having dealt with points and lines in our previous episode, we make a leap to two-dimensional shapes in today's entry as we move on to "The Areas", the next Calendars, Clocks, and Calculators script. Crafted by Chris Key in April 1998, the Areas script calculates the areas of a triangle, a circle, a rectangle, a trapezoid, a rhombus, and an "oval" (an ellipse) from various length inputs; at no extra charge, it similarly calculates the volume of a "rectangular prism" (a rectangular cuboid) in a brief foray into three-dimensionality.

The areas.html page sports a functioning demo (a couple of the calculations need some help, but we'll get them sorted out); the areas.txt Grab the Script page is still available.

Body HTML

The area/volume determinations are discretely carried out by the seven functions of a <script> element in the document head, in source order:
areati( ), areac( ), arear( ), areata( ), arearh( ), areao( ), volumerp( )
Each function is called by clicking a push button. There's nothing particularly noteworthy about the button HTML but here it is anyway:

<center>
<form>
<h3>Area of polygons!</h3>
<input type="button" value="Area of triangle" onclick="areati( );">
<input type="button" value="Area of circle" onclick="areac( );">
<input type="button" value="Area of rectangle" onclick="arear( );">
<p>
<input type="button" value="Area of trapezoid" onclick="areata( );">
<input type="button" value="Area of rhombus" onclick="arearh( );">
<input type="button" value="Area of oval" onclick="areao( );">
<hr>
<h3>Volume of three-dimensional figures</h3>
<input type="button" value="Volume of a rectangular prism" onclick="volumerp( );">
</center>
</form>

Note that the <center> and <form> elements are improperly nested. The <center> container is obsoleted by HTML5: replace it with a <div style="text-align:center;"> container. The <form> container is unnecessary today although Netscape 4.x users did in fact need it to render the <input>s back in the day.

The body display is missing an all-important something, and that something would be pictures of the various shapes. OK, we all know what a triangle, a circle, and a rectangle look like. However, if someone asked you to draw a trapezoid or a rhombus, could you do it? I myself wouldn't have been able to do so prior to writing this post, so I will supplement the discussion below with some relevant shape clip art: credits therefor will be given at the end of the post.

Areas sans ovals

Triangles

Clicking the button calls the areati( ) function.

<script language="javascript">

function areati( ) { var base = window.prompt("Enter the base"); var height = window.prompt("Enter the height"); var sum = (1 / 2) * base * height; window.alert("The area is " + sum + ""); } A first prompt( ) box asks the user to enter (the length of) the base of a triangle; a second prompt( ) box asks the user to enter the triangle's height. For the triangle above, the base is the distance spanning points A and C and the height is the h distance spanning point B and the AC base. The user's inputs are subsequently plugged into an A = ½ × b × h formula and the resulting area is displayed on an alert( ) box.

N.B. As demonstrated by this Khan Academy video, the A = ½ × b × h formula is applicable to all triangles and not just equilateral, isosceles, and right triangles. For other ways to calculate the areas of triangles, check out MathsIsFun.com's "Area of Triangles Without Right Angles" page.

General function commentary:
(1) That the prompt( ) returns have a string data type poses no problem for the area calculation.
(2) No validation is provided for the user's inputs, i.e., nothing shields the area calculation from unwanted strings, negative numbers, or the null literal.
(3) Curiously, the area is stored in a sum variable - as area itself is neither a JavaScript reserved keyword nor a JavaScript future reserved keyword, why not use that?
(4) The + "" operation in the alert( ) argument is unnecessary.
Comment (1) applies to all the functions except the areata( ) function. Comments (2), (3), and (4) apply to all seven functions.

Circles

Clicking the button calls the areac( ) function.

function areac( ) { var radius = window.prompt("Enter the radius"); var pi = "3.14159"; var sum = radius * radius * pi; window.alert("The area is " + sum + ""); } A prompt( ) box asks for a circle radius; the radius is plugged into an A = πr2 formula; an alert( ) box displays the resulting area. I would use Math.PI rather than 3.14159 for the π value but to each his own. (Ideally, you could call up Kate Bush and she would sing the digits of π to you but I understand she's pretty busy most of the time. ;-))

Rectangles

function arear( ) { var length = window.prompt("Enter length"); var width = window.prompt("Enter width"); var sum = width * length; window.alert("The area is " + sum + ""); } Length times width. I don't need to belabor this for you, do I? Didn't think so.

Trapezoids

In the US and Canada, a trapezoid is a quadrilateral having two parallel sides, which are termed the bases of the trapezoid. • Can the non-base sides of a trapezoid also be parallel? Some say yes, some say no.
• In the UK, Australia, and New Zealand, a trapezoid is a quadrilateral having no parallel sides whereas a trapezium is a quadrilateral having two parallel sides. (My guess is that Guyana and the British Overseas Territories in the Western Hemisphere also use these definitions but I don't know for sure.)

The area of a US/Canada trapezoid is given by: A = the average of the trapezoid base lengths × the trapezoid height. The areata( ) function

function areata( ) { var baseone = window.prompt("Enter base one"); var basetwo = window.prompt("Enter base two"); var height = window.prompt("Enter height"); var sum = (1 / 2) * height * (baseone + basetwo); window.alert("The area is " + sum + ""); }

would seem to calculate this area, but it doesn't: here we run into a problem in not numberifying the prompt( ) returns, specifically, the baseone and basetwo values are concatenated and not added at the var sum = (1 / 2) * height * (baseone + basetwo); line. Recasting the baseone and basetwo definitions as

var baseone = Number(window.prompt("Enter base one"));
var basetwo = Number(window.prompt("Enter base two"));

sets things to right in short order.

Rhombuses

A rhombus is a quadrilateral whose side lengths are all equal; squares are rhombuses but not all rhombuses are squares. We can get the area of a rhombus by multiplying the lengths of its diagonals (the distances spanning points A and C and points B and D for the above rhombus) and dividing the product by 2, and the arearh( ) function does just that:

function arearh( ) { var onediag = window.prompt("Enter the 1st diagonal"); var twodiag = window.prompt("Enter the 2nd diagonal"); var sum = onediag * twodiag * (1 / 2); window.alert("The area is " + sum + ""); }

For other ways to calculate the area of a rhombus, check out the Area section of Wikipedia's "Rhombus" entry.

Image credits so far

(1) I got the triangle image at a Mathematics Stack Exchange page.
(2-4) The circle image, the rectangle image, and the trapezoid image were created by the Florida Center for Instructional Technology at the University of South Florida.
(5) The rhombus image appears courtesy of AnalyzeMath.com.
Thank you, one and all. As for the image of the quadrilateral with no parallel sides, I created that one myself using the ChemDraw program cited in Blog Entry #128.

We'll go through the areao( ) and volumerp( ) functions, and roll out a demo, in the next entry.

Sunday, February 14, 2016

From Point A to Point B
Blog Entry #360

We've got some simple geometry on tap today as we move on to "Slope", the next Calendars, Clocks, and Calculators script. Authored by Michael Finnegan in October 1998, the Slope script asks the user to input two pairs of Cartesian coordinates - we'll call them (x1,y1) and (x2,y2) - and then outputs the slope, y-intercept, and equation of the line that runs through those coordinates. A functioning demo is built into the Java Goodies Slope page - just follow the introductory link above and you'll get it.

To access the Slope script, you can either
(a) fish it out of the www.javagoodies.com/slope.html source (the Grab the Script page to which the main page linked is gone) or
(b) get it at the JavaScript Goodies site.
The script contains one <script> element and no other HTML outside or written by that element: it's pure JavaScript for all practical purposes.

Input interface

The script solicits the x1, y1, x2, and y2 coordinates via a set of four prompt( ) commands.

<script language="javascript">
var x1 = prompt("What would you like coordinate x1 to be?");
var y1 = prompt("What would you like coordinate y1 to be?");
var x2 = prompt("What would you like coordinate x2 to be?");
var y2 = prompt("What would you like coordinate y2 to be?");

Actually, prompt( ) is a bona fide method of the window object and not a top-level function, but you knew that, right? Mozilla's current window.prompt( ) page is here; the W3C has standardized window.prompt( ) via HTML5; we for our part first discussed window.prompt( ) all the way back in Blog Entry #11 (there are no Forbiddens in this one but it does have its share of dead links).

m and b

The Δy/Δx slope of the line passing through our coordinates is calculated with:

var y3 = y1 - y2;
var x3 = x1 - x2;
var slope = y3 / x3;
/* Of course, this can be written as a single line: var slope = (y1 - y2) / (x1 - x2); */

Not mentioned in Blog Entry #11: the prompt( ) return has a string data type, which doesn't pose a problem for the preceding subtraction operations although it is fair enough to numberify the prompt( ) returns via the Number( ) function if you feel that doing so adds clarity to the code.

var x1 = Number(window.prompt("What would you like coordinate x1 to be?"));
var y1 = Number(window.prompt("What would you like coordinate y1 to be?"));
// Etc.

With the slope of the line in hand, we can plug either (x1,y1) or (x2,y2) into the y = mx + b line formula and solve for b to get the line's y-intercept. Here's the script's code for doing so:

var z1 = slope * x1;
var z2 = -1 * y1;
var z3 = z1 + z2;
var z4 = -1 * z3;
/* This can also be written as a single line: var z4 = y1 - slope * x1; */

As for the Δy and Δx subtractions, the var z1 = slope * x1; and var z2 = -1 * y1; multiplications return numbers and therefore we get addition and not concatenation with the var z3 = z1 + z2; line.

Output interface

The script displays the slope, y-intercept (z4), and equation of the line on a pair of alert( ) boxes.

alert("The slope of the line formed by points " + x1 + "," + y1 + " and " + x2 + "," + y2 + " is " + slope);
alert("The y intercept of this line is " + z4 + ". The equation of the line is: y=" + slope + "x+" + z4);
/* alert( ) is also a bona fide method of the window object. */
</script>

Validation 1

What if the user types hi instead of a number in a prompt( ) field? What if the user leaves a prompt( ) field blank? What if the user clicks the button on a prompt( ) box? The original script makes no effort to validate the user's inputs, so maybe we should do that, huh?

Consider the prompt("What would you like coordinate x1 to be?") box/field: if the user enters hi into the field, leaves the field blank, or clicks the button on the box, then x1 will be hi, the empty string, or a null literal (vis-à-vis a null string), respectively. We can intercept these returns via a while loop that nags the user to enter a number so long as x1 isn't a number.

while (isNaN(x1) || x1 === "" || x1 === null) x1 = window.prompt("Your x1 coordinate must be a number:");

The x1 === "" and x1 === null subconditions are necessary because isNaN("") and isNaN(null) both return false (Number("") and Number(null) both return 0), or at least that's what I see with Firefox, Google Chrome, and Opera on my computer.

Alternatively, we can stop unwanted x1s AND bound the number of digits for numerical x1s via a single regular expression-based condition, e.g.:

while (!/^-?\d{1,2}(\.\d{1,2})?\$/.test(x1)) x1 = window.prompt("Please input an x1 coordinate in the range -99.99 to +99.99, inclusive:");

A grounded I/O

I don't like the prompt( ) input interface: if it were up to me, we would more conventionally log the x1/y1/x2/y2 coordinates via a set of text inputs.

<div id="slopeDiv">
<label>Input your x<sub>1</sub> coordinate: <input name="" size="5"></label><br>
<label>Input your y<sub>1</sub> coordinate: <input name="" size="5"></label><br>
// Etc.

FYI: When writing out x1/y1/x2/y2, I prefer to subscript the 1 and 2. In an HTML context, 1 and 2 are subscripted with the sub element; in a JavaScript context (see the vetting code below), we can code a subscripted 1 and 2 via the Unicode sequences \u2081 and \u2082, respectively.

Do we need a <form> container here? Nope, not if we're not gonna submit the input data to a processing agent of some sort. We can collectively get the <input>s with:

function slope_and_intercept( ) { var coordInputs = document.getElementById("slopeDiv").getElementsByTagName("input");
...
<button type="button" onclick="slope_and_intercept( );">Get the slope and y-intercept</button>

With the coordInputs HTMLCollection in hand, it's not necessary to hold onto the x1/y1/x2/y2 variables although there's no harm in keeping them, either.

var x1 = coordInputs.value;
var y1 = coordInputs.value;
var x2 = coordInputs.value;
var y2 = coordInputs.value;

The coordInputs values can be vetted automatedly:

var coordArray = ["x\u2081", "y\u2081", "x\u2082", "y\u2082"]; for (var i = 0; i < coordInputs.length; i++) { if (!/^-?\d{1,2}(\.\d{1,2})?\$/.test(x1)) { window.alert("Your " + coordArray[i] + " coordinate must be a number in the range -99.99 to +99.99, inclusive."); coordInputs[i].value = ""; coordInputs[i].focus( ); return; } }

(It occurred to me that inputted values could be checked on the fly via an inputObject.onchange = validation; function; however, we wouldn't be able to flag unchanged blank values with this approach.)

The slope and y-intercept determinations can give numbers with a lot of post-decimal point digits - two such digits is quite enough for our purposes, wouldn't you say?

var slope = (y1 - y2) / (x1 - x2);
var z4 = y1 - x1 * slope;
if (/\.\d{3,}\$/.test(slope)) slope = slope.toFixed(2);
if (/\.\d{3,}\$/.test(z4)) z4 = z4.toFixed(2);

The alert( ) output interface also rubs me the wrong way, so once we calculate slope and z4 as detailed above, let's write the line data to <samp>s on the page.

/* This is to be the last clause of an if...else if...else cascade whose if and else if clauses are defined in the Vertical lines, horizontal lines, and points subsection below. */ else { document.getElementById("slopeSamp").textContent = "The slope of the line formed by the points (" + x1 + "," + y1 + ") and (" + x2 + "," + y2 + ") is: " + slope; document.getElementById("y_interceptSamp").textContent = "The y-intercept of this line is: (0," + z4 + ")"; if (slope == 1) slope = ""; if (slope == -1) slope = "-"; if (z4 > 0) var y_interceptString = " + " + z4; if (z4 < 0) y_interceptString = " - " + Math.abs(z4); if (z4 == 0) y_interceptString = ""; document.getElementById("equationSamp").textContent = "The equation of this line is: y = " + slope + "x" + y_interceptString; }
...
<samp id="slopeSamp"></samp><br>
<samp id="y_interceptSamp"></samp><br>
<samp id="equationSamp"></samp>
</div>

• I want parentheses to surround the points so I put them in there; moreover, I want a (0,z4) y-intercept rather than just a number.
• If the slope is 1 or -1, then we don't need to put the 1 digit in the equation: the if (slope == 1) slope = ""; if (slope == -1) slope = "-"; conditionals lose the 1 in these cases. The if (z4 == 0) y_interceptString = ""; conditional similarly loses the equation's y-intercept if it's 0.

Reset

Naturally, we want to add a reset capability to the display; in the absence of a <form>, here's how we would do that:

function reset( ) { var coordInputs = document.getElementById("slopeDiv").getElementsByTagName("input"); for (var i = 0; i < coordInputs.length; i++) coordInputs[i].value = ""; var outputSamps = document.getElementById("slopeDiv").getElementsByTagName("samp"); for (var i = 0; i < outputSamps.length; i++) outputSamps[i].textContent = ""; /* You'll want to clear the outputSamps textContents when running the slope_and_intercept( ) function as well. */ }
...
<button type="button" onclick="reset( );">Reset</button>

Vertical lines, horizontal lines, and points

For a vertical line, x1 and x2 are equal and the x1 - x2 difference is 0. With the original script, inputting (1,1) and (1,2) leads to a 'line' whose slope, y-intercept, and equation are -Infinity, Infinity, and y=-Infinityx+Infinity, respectively - not exactly a meaningful outcome, is it? Here's how I would deal with vertical lines given that
(a) division by 0 is actually undefined and
(b) vertical lines don't have y-intercepts, excepting the x = 0 line that coincides with the y-axis and effectively has an infinite number of y-intercepts:

else if (x1 == x2) { document.getElementById("slopeSamp").textContent = "The slope of the line formed by the points (" + x1 + "," + y1 + ") and (" + x2 + "," + y2 + ") is: Undefined/infinity (it's a vertical line)"; document.getElementById("y_interceptSamp").textContent = x1 == 0 ? "This line coincides with the y-axis." : "There is no y-intercept."; document.getElementById("equationSamp").textContent = "The equation of this line is: x = " + x1; }

For a horizontal line, y1 and y2 are equal and the y1 - y2 difference is 0. We don't run into any division-by-0 problems in this case (unless x1 and x2 are also equal, see the point case below) although I would still tweak the output messages a bit:

else if (y1 == y2) { document.getElementById("slopeSamp").textContent = "The slope of the line formed by the points (" + x1 + "," + y1 + ") and (" + x2 + "," + y2 + ") is: 0 (it's a horizontal line)"; document.getElementById("y_interceptSamp").textContent = "The y-intercept of this line is: (0," + y1 + ")"; document.getElementById("equationSamp").textContent = "The equation of this line is: y = " + y1; }

If x1 and x2 are equal AND y1 and y2 are equal, then we have a point rather than a line.

if (x1 == x2 && y1 == y2) document.getElementById("slopeSamp").textContent = "Houston, this isn't a line - it's a single point located at: (" + x1 + "," + y1 + ")";

Left unchecked, a 0 / 0 division would give NaN returns for the slope and y-intercept - not what we want, needless to say.

Demo

OK, let's try it all out.

Welcome to The Line Forum. Give us two pairs of Cartesian coordinates and we'll give you a slope, y-intercept, and equation.

Next up: "The Areas"