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

**Forbidden**s 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[0].value;`

var y1 = coordInputs[1].value;

var x2 = coordInputs[2].value;

var y2 = coordInputs[3].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=-`, respectively - not exactly a meaningful outcome, is it? Here's how I would deal with vertical lines given that

`Infinity`x+`Infinity`(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"

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)