Tuesday, 13 October 2020

Saturday, 13 June 2020

Are we nearly there yet?


I have suffered (may be too strong a word) a sequence of changed aspirations for a simple development task. This is not uncommon in the realms of corporate IT departments where projects often grow willy-nilly to a point where they ultimately collapse under the weight of their own ambition. That was less critical.

I set out to do a straightforward demonstration of programming SVG from JavaScript. This topic did not get any coverage in my book on learning to program JavaScript but felt afterwards to be an omission for anyone who read that book primarily to develop the basics of HTML5 game programming. This aspiration coincided with my rediscovery of a slide rule that had been among my late Father’s effects.

It turned out that drawing slide rule scales using SVG was effective but proved a poor demonstration of SVG in general. So I wrote a blog post that went on from the introduction to tackle more of SVG. I still wanted to complete the slide rule though with the eventual result of a virtual and interactive version.

Inevitably, I went further and added some displays to clarify the slide rule “output” during its operation. Not content with that I then added some sample problems that (with a bit of user interaction) could demonstrate how slide rules were used for anyone born much after the middle 1960s. Job done, so I posted the web page for anyone who might be interested.

Trouble was – the web page started to niggle. Things had shifted from demonstrating SVG, through demonstrating a slide rule to the quality of the web page as an interactive demonstration. It fell a little short.

The basics were OK. The slide rule worked fine. The display of scale values needed a tweak or two to better match the ability of a human to estimate positions between rule graduations at different locations along a given scale. The big issue was the invitation to the page visitors to try some specific slide rule based calculations. The most obvious paradigm for the modern age would be an electronic calculator but I had ducked that the first time around as “off topic”. It was time to revisit that decision.

The new version was also a good opportunity to clarify that tangents for angles greater than 45 degrees could be found using the existing scale (well up to 84 degrees anyway) and that the S scale can be used to obtain cosines. I also added log10(x) and 10x (antilogarithm) function keys to the original trial set to exercise the L scale and because (if you are happy to play with logs) you can get other powers and roots of numbers.

The virtual calculator probably has some idiosyncrasies and it should certainly be remembered that it is only as accurate as the virtual slide rule. Results can and will differ from your personal calculator or spreadsheet.

Thursday, 11 June 2020

My Interactive Slide Rule

An earlier post entitled "An introduction to SVG - part 1" quickly wandered off topic to discuss slide rules and how they applied logarithmic scales to facilitate calculations before the invention of affordable electronic calculators. It was the idea of constructing a slide rule image that gave me the idea of writing an introduction to SVG. As it turns out, a slide rule is not a great project for developing general SVG skills. So part two of the SVG introduction dealt with a wider range of graphical challenges leaving my slide rule project to end up here.

Creating SVG XML code from JavaScript is an effective way of drawing slide rule scales but drawing them on a <canvas> object would have been almost as easy although SVG has some very nice text positioning options. The downside of SVG for this project was probably the huge amount of XML generated and inserted into the DOM. The end result of my endeavours can be found at https://adit.co.uk/sliderule.html but that is getting the end of the development process in before the start.

The project started with an HTML page containing just four empty <divs>. These were styled using CSS to create the four main components of a slide rule - the upper stator, lower stator, slide and cursor. I then used jQuery to ease the path of getting the slide and cursor to move in response to a mouse or the keyboard left/right arrow keys. That was the foundation.

Once the slide rule representation was animated I could attend to drawing the slide rule scales using SVG, I am sure that most slide rule users (back in the day) had rules with scales they did not use and probably wished for a scale that was omitted. Rules were sold with sensible groupings to suit particular usage and some high quality rules were reversible so that additional scales could be accommodated. I was largely guided in my choices by the slide rule I had inherited from my Father. I did though, switch one of the L scales for a K scale. It was because I was generally copying that slide rule that the cursor image became tinted to reflect the ageing of the original plastic.

Drawing a log scale is no more difficult than drawing a linear scale and the calculation was demonstrated in the first post here on SVG. The trigonometrical scales are reversed as the sine of 90 degrees and the tangent of 45 degrees is 1 and the log10 of 1 is zero. I could have tried adding cosine scale values below the sine scale (S) as the intervals would be the same but with cos(90) at the left hand end. Similarly I also declined the visual confusion of adding tangent scale values in the range 45 degrees to 84 degrees (approx). These are not shown on my Father's slide rule but I think I had them on the one I owned years ago.


Having built my SVG slide rule and found that I could use it for some straightforward calculations I wondered how the demonstration might be improved. I then added a visual display of the values on each scale under the cursor hairline plus the values at the points where the A and B scales and CD scales intersected (well at the lower end).


I then started dreaming about an interactive calculator with the slide rule being used to manage the calculations. I simplified that idea by laying out a set of typical problems where the page user can enter values, click a button and see the slide rule positioned to manage the calculation and display the result.


You can use this virtual slide rule with the slide and cursor being positioned for you aromatically. How about that for labour saving?

There might be a temptation for the code to "cheat" when running these calculations but they are all managed by positioning the slide and cursor and "reading back" the cursor position and applying the antilogarithm [result = Math.pow(10, x)] to get the value for each scale.

The challenge of automating the calculations was mostly one of keeping track of the magnitude of the result. This was something that people were adept at, back when log tables were commonly used in schools. The square root and cube root calculations were the most interesting as a decision has to be made on which part of the A and K scales respectively the calculation should be performed.

As an example, the square root of 4 is 2 and the square root of 40 is close to 6.32 and these calculations are easy to set on the A scale with the result read from the D scale. However the square root of 400 is 20 so that calculation is performed back at the low end of the A scale while 4,000 returns to the upper end. The decision for any given value is based upon the number of digits before the decimal point for values greater than or equal to 1 or the number of zeros following the decimal point for values less than 1. Odd numbers of digits are managed at the lower end while even numbers of digits are tackled at the upper end of the A scale. The position on the K scale for cube root calculations is also based on the number of digits (modulo 3).

All of the code (with the usual advance apologies) is accessible using your browser "Developer Tools" if any of it is of interest. Feel free to comment on bugs or missed opportunities (I am still thinking about that calculator as it would look cool).

<Edit>
Well I thought about the virtual calculator option and decided to implement it here. Enjoy.

My thought on the process (trivial though such thoughts and process are) can be found here.
dated: 13/06/2020
</Edit>

Friday, 5 June 2020

An introduction to SVG - part 1

I little while back I blogged about recreating the Concentrichon using JavaScript and canvas drawing rather than the SVG of the original. In that post I mentioned that my JavaScript book had only built games using canvas drawing and I wondered now if readers of that book would be interested in a general introduction to SVG. There are many instances where SVG rather than canvas drawing would be the most appropriate tool for displaying and animating particular images.

SVG stands for Scalable Vector Graphics and it is a 2D image format that uses XML (Extensible Markup Language) syntax. Two TLAs in that sentence but there you go. Vector Graphic images are defined in terms of points on a plane with lines and curves joining the points. As the relative position of each point can be adjusted then the image can be scaled without degradation. Individual lines and curves can have defined colour, thickness and fill. Each time such an image is rendered then it is drawn afresh from the XML definition. SVG animations are baked into the specification and many effects can be achieved without program code.

My introduction to the JavaScript programming of SVG images starts with a project but veers off (many, probably most, will be glad to know) to cover some wider aspects of this graphics format than would be exercised by my wanting to reproduce an interactive version of that analogue calculating device known as a slide rule.

<Aside topic=”Slide Rules”>

Feel free to skip this section if you just want to start in on some SVG.

I was looking through some bits and bobs that came from a draw in my Father’s house when it was being cleared for sale. One of the items was a slide rule and it was found alongside the early “Sinclair Executive” calculator that replaced it. It is small (nominally 6” probably) and pretty similar to the one I remember buying with my first student grant money in the very late 1960s when they were still pretty much the state of the art for calculators. Around that time though, I did come into contact with an electronic Anita calculator but they were staggeringly expensive and you could hardly slip one in your pocket or even carry one very far.

The Sinclair Executive calculator came out in 1972 and cost £79.95 (over £1000 in 2020 money) and I probably paid £5 or £6 for the slide rule three or four years before that (still an appreciable cost that needed some internal debate to justify).

The slide rule was invented sometime between 1620 and 1630 with new functions developed and added over time until the device became the tool of choice for the developing field of engineering.

How do they work? It is pretty easy to see how two rulers could be used to do simple addition and subtraction. We can try adding 3 and 8.


We position the start on the lower ruler’s scale at one of the two values (in this case 3) and read off the sum of 3 and 8 on the scale of the upper ruler (see the red line). It should also be obvious that the same positioning could be used to calculate 11 minus 8 or any other pair of values on the two ruler scales.

Now such a simple mechanical device for adding two numbers together would not be terribly useful even if they were decimal fractions such as 3.4 and 8.7 which would be easy to do with the rulers shown above. However being able to multiply (say) 1.65 by 3.45 would be more challenging mental arithmetic for most. When I was at school, we used logarithms for such calculations.

The logarithm (base 10) of a number is the number expressed as a power of 10.

1.65 = 100.2175  so log(1.65) = 0.2175

3.45 = 100.5378 so log(3.45) = 0.5378

0.5378 + 0.2175 = 0.7553 (sum the logs)

100.7553 = 5.6925 which is the product of 1.65 and 3.45.

Thus simple addition can be used to multiply two decimal fractions.

Fortunately, when I was at school, we did not need to calculate these powers of 10 – we were issued with books of tables for looking them up. The tables included a host of trigonometric tables as well as the vital “antilogarithms” needed to establish that 100.7553 was 5.69 (which was about as accurate as the ones I had could get.

If instead of having pages of tables we were to draw a logarithmic scale on a pair or rulers instead of the linear scale illustrated above we could use them to multiply two values by adding the logs. Indeed we could also do division by subtracting one logarithmic scale position from another. So, what does a logarithmic scale look like in action?

You will notice that as the values increase (from 1 to 10 in this instance) the distance between the log of those values decreases. You can probably also see that two rules with logarithmic scales can be used to make our calculation. Slide rules were fast and accurate enough for most purposes.

Why does the log scale start at 1? Well ten to the power of zero is 1 and zero is a good place to start. In fact any number to the power of zero is 1. Feel free to debate about zero to the power of zero; Wikipedia has a page on it.

As it was way easier to use JavaScript and SVG to create the illustrations in this “aside” than to draw them using something like Paint.NET it is probably time now to cut back to the chase as they say.

</Aside>

We could start some SVG development with an HTML page and a short CSS file.

main.css looks like:

html { padding: 0; margin: 0; border: 0;} #dvViewer { width: 300px; height: 200px; } svg { height: 100%; width: 100%; }

and the index.html file has all the SVG action and plagiarises the Mozilla documentation (at least in part).

<!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <meta http-equiv="X-UA-Compatible">     <title>Basic SVG Demo</title>     <meta name="viewport" content="width=device-width, initial-scale=1">     <link rel="stylesheet" type="text/css" media="screen" href="main.css" /> </head> <body>     <div id="dvViewer">         <svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">             <style>              .normal { font: normal 15px sans-serif; }              .bold { font: bold 30px sans-serif; }              .bright { font: italic 40px serif; fill: red; }             </style> <!-- font colour is the fill -->             <text x="10" y="35" class="normal">Welcome</text>             <text x="75" y="35" class="bold">readers</text>             <text x="55" y="65" class="normal">to</text>             <text x="70" y="65" class="bright">SVG!</text>         </svg>     </div> </body> </html>

The HTML <body> contains a single <div>. Within the <div> there is an <svg> element that is structured in a pretty familiar way. XML is a “markup language” just like HTML. The <svg> element contains a <style> tag that defines three classes that can be used for CSS type styling. The <style> object is followed by four text objects with the “x” and “y” attributes setting the bottom left for the text content. The “x” and “y” units relate to the “viewBox” attribute that sets the dimensions of the viewport.

If you load the HTML file into a browser then you should see:

The <svg> viewBox attribute read   viewBox="0 0 200 100". This defined the upper left hand corner as having coordinates 0,0 with a width of 200 units and a height of 100 units. This is not the same as the dimensions of the <svg> object set by the CSS. Try resetting the viewBox values so that they match the size (300 by 200) set in the CSS and reload the page. You should observe some instant scaling.

Next step is to try drawing something just a little bit technical. Add another <div> and some enclosed <svg> to the HTML file.

<div id="dvScale">     <svg viewBox="0 0 510 30" xmlns="http://www.w3.org/2000/svg">         <style>             .normals { font: normal 10px sans-serif; text-anchor: middle;}             .tl {stroke: black; stroke-width: 0.5;}         </style>         <line x1="5" y1="20" x2="5" y2="30" class="tl" />         <text x="5" y="19" class="normals" >1</text>         <line x1="155.5" y1="20" x2="155.5" y2="30" class="tl" />         <text x="155.5" y="19" class="normals" >2</text>         <line x1="243.5" y1="20" x2="243.5" y2="30" class="tl" />         <text x="243.5" y="19" class="normals" >3</text>         <line x1="306" y1="20" x2="306" y2="30" class="tl" />         <text x="306" y="19" class="normals" >4</text>         <line x1="354.5" y1="20" x2="354.5" y2="30" class="tl" />         <text x="354.5" y="19" class="normals" >5</text>         <line x1="394" y1="20" x2="394" y2="30" class="tl" />         <text x="394" y="19" class="normals" >6</text>         <line x1="427.5" y1="20" x2="427.5" y2="30" class="tl" />         <text x="427.5" y="19" class="normals" >7</text>         <line x1="456.5" y1="20" x2="456.5" y2="30" class="tl" />         <text x="456.5" y="19" class="normals" >8</text>         <line x1="482" y1="20" x2="482" y2="30" class="tl" />         <text x="482" y="19" class="normals" >9</text>         <line x1="505" y1="20" x2="505" y2="30" class="tl" />         <text x="505" y="19" class="normals" >10</text>     </svg>     </div>

and back that up with a small addition to the CSS file.

#dvScale {     width: 510px;     height: 30px; }

Reload the HTML file in your browser and you should see the new addition rendered like.

Clearly that was a lot of XML added to the web page and it should also be fairly clear that some calculations had been hand cranked to place the vertical lines and numbers. This would be much better achieved using code so the next step will be to switch to creating our SVG graphical output using JavaScript. Before that though, take a look at the two new classes added between the <style> tags. The text style (normals) has a text-anchor attribute to centres the numbers over their defined "x" position. The <line> tags are probably what you would expect with a defined start and end point. One thing though, the style class applied defines the stroke colour as if this is omitted then the line will not be drawn.

The next step minimises the HTML but adds a couple of short JavaScript files.

<!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <meta http-equiv="X-UA-Compatible">     <title>SVG and JavaScript</title>     <meta name="viewport" content="width=device-width, initial-scale=1">     <link rel="stylesheet" type="text/css" media="screen" href="main.css" />     <script src="svg.js"></script> </head> <body>     <div id="dvScale">     </div>     <script src="main.js"></script> </body> </html>

There is a very good chance that you will decide to use a JavaScript library to manipulate SVG graphics. [If there is a lot of HTML to manipulate then you might well decide to make use of jQuery so similarly, if a lot of SVG is going to be created then a library designed to make that simpler is likely to appeal.] As a placeholder for such a library, I created a JavaScript file called svg.js with one function,

function createSvgElem(svgTag, svgAttr, svgStyle, addTo) {     const nSpace = "http://www.w3.org/2000/svg";     let nElem = document.createElementNS(nSpace, svgTag);     for (let atrib in svgAttr) {         nElem.setAttributeNS(null, atrib, svgAttr[atrib]);     }     if (svgStyle) {         for (let atrib in svgStyle) {             nElem.style[atrib] = svgStyle[atrib];         }     }     if (addTo) {         addTo.appendChild(nElem);     }     return nElem; }

That function is passed an SVG tag name, an object containing relevant attributes, an object containing style attributes (or null if there are none) and an object to which the new SVG object created should be added. For the main <svg> object that will be an HTML element but things like <line> and <text> tags will be added to a preexisting <svg> element.

All the creative action is in the main.js file.

var div = document.getElementById("dvScale"); function initialise(){     let svgStyle = {         small: { "font": "normal 10px sans-serif", "text-anchor": "middle"},         tl: {"stroke": "black", "stroke-width": "0.5"}     };     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     for(let i = 1; i < 11; i++){         let x = (Math.log10(i) * 500 + 5).toFixed(1); // log of number * scale length + scale start         svgAttr = {"x1": x, "x2": x, "y1": "20", "y2": "30"};         createSvgElem("line", svgAttr, svgStyle.tl, svg);         let lab = createSvgElem("text", {"x": x, "y": "19"}, svgStyle.small, svg);         lab.innerHTML = i.toString();     } } initialise();

That code takes a bit of a  scatter gun approach to show a couple of techniques for attributes and styles. The <svg> object is created with an attribute detailing the viewBox. Then a sequence of <line> and <text> tags are added within the for loop but note that the content of each <text> tags is added after it is created.

If you open the revised web page in your browser and then open the "Developer Tools" (<F12>) you can take a look at the content of the <div> with the id dvScale using the "elements" tab in Chrome and Edge or the "Inspector" tab in FireFox. You should see the nicely laid out <svg> tag that starts:

<svg viewBox="0 0 510 30">   
    <line x1="5.0" x2="5.0" y1="20" y2="30" style="stroke: black; stroke-width: 0.5;"></line>
    <text x="5.0" y="19" style="font: 10px sans-serif; text-anchor: middle;">1</text>
    <line x1="155.5" x2="155.5" y1="20" y2="30" style="stroke: black; stroke-width: 0.5;"></line>
    <text x="155.5" y="19" style="font: 10px sans-serif; text-anchor: middle;">2</text>
    <line x1="243.6" x2="243.6" y1="20" y2="30" style="stroke: black; stroke-width: 0.5;"></line>
    <text x="243.6" y="19" style="font: 10px sans-serif; text-anchor: middle;">3</text>
    <line x1="306.0" x2="306.0" y1="20" y2="30" style="stroke: black; stroke-width: 0.5;"></line>
    <text x="306.0" y="19" style="font: 10px sans-serif; text-anchor: middle;">4</text>
    <line x1="354.5" x2="354.5" y1="20" y2="30" style="stroke: black; stroke-width: 0.5;"></line>
    <text x="354.5" y="19" style="font: 10px sans-serif; text-anchor: middle;">5</text>
...continues...

Looking just as it might have been typed in the hard way.

Enough of these logarithmic scales - time to sample some of the other things we can do with SVG - all in part 2.


Further reading:



SVG JavaScript Libraries (web search for others):

Snap.svg 


An introduction to SVG - part 2

Part 1 explored creating SVG graphics using XML embedded in a web page and then followed that up with some JavaScript that generated the SVG to duplicate the original output. This part will follow on from those simple <text> and <line> tags with some more SVG goodies starting with some shapes.

Assuming that you are following on from part 1 we can keep the current HTML, CSS and svg.js files and try out some SVG shapes with changes to the main.js file.

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     svgAttr = {x: 2, y: 2, width: 30, height: 20};     createSvgElem("rect", svgAttr, null, svg); } initialise();

Which gets us a solid black rectangle. Looks like we need some style.

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     svgAttr = {x: 2, y: 2, width: 30, height: 20};     let svgStyle = {stroke: "lime", fill: "white"};     createSvgElem("rect", svgAttr, svgStyle, svg); } initialise();

We could also set the stroke-width and you are likely to often want a transparent fill. Try using:

let svgStyle = {stroke: "lime", fill: "transparent", "stroke-width": 3};

Rounded corners? Specify the corner in the style object.

let svgStyle = {stroke: "black", fill: "transparent", "stroke-width": 3, rx: 5, ry: 5};

For a circle we are going to have to specify the centre and the radius while an ellipse effectively specifies two radii (one for the vertical y dimension and the other for the horizontal).

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     svgAttr = {cx: 20, cy: 15, r: 12};     let svgStyle = {stroke: "hotpink", fill: "transparent", "stroke-width": 1};     createSvgElem("circle", svgAttr, svgStyle, svg);     svgAttr = {cx: 60, cy: 15, rx: 12, ry: 6};     createSvgElem("ellipse", svgAttr, svgStyle, svg); } initialise();

A polyline has multiple x/y coordinates expressed as a single attribute. You will need to decide how you will ensure that the specification is easy to maintain, particularly if a given line is long. I chose to have the x and y values separated by a space and each point separated by a comma (OK and a space).

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     svgAttr = {points: "0 0, 10 10, 0 20, 30 20, 40 10"};     let svgStyle = {stroke: "teal", "stroke-width": 1};     createSvgElem("polyline", svgAttr, svgStyle, svg); } initialise();

But that code draws:

The polyline is (perhaps unexpectedly) filled, so we probably need to be more specific. Change the style object as follows and give it another run.

let svgStyle = {stroke: "teal", "stroke-width": 1, fill: "transparent"};

The SVG polygon looks just like a <polyline> but there is an implicit line drawn from the last specified point back to the first, to enclose the shape.

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 30"};     let svg = createSvgElem("svg", svgAttr, null, div);     svgAttr = {points: "15 0, 0 10, 5 30, 25 30, 30 10"};     let svgStyle = {stroke: "red", "stroke-width": 1, fill: "transparent"};     createSvgElem("polygon", svgAttr, svgStyle, svg); } initialise();

The <polyline> and <polygon> tags bring us to the even more flexible but potentially complex <path>.

The specification for a <path> tag "d" attribute is akin to a language as it can contain a sequence of commands. The command sequence is used to define a set of lines and curves. For an explanation of the individual command options I recommend reading the Mozilla page here.

In brief. The command designated by the uppercase letter "M" is a "move to" command with a specified x/y coordinate. A lower case "m" specifies a relative movement from the current location. L and l define drawn lines to an absolute or relative location. H and h draws a horizontal line with just an x location specified. Similarly V and v draw vertical lines with just a y value. Z or z closes a path by drawing a line back to the start point. C and c commands can be used to draw Cubic Bezier curves. Similarly S, Q and T will draw alternate Bezier curve types. A and a can be used to draw arcs. All of the path components form a contiguous whole.

You are probably not going to hand code (or edit XML for) lengthy <path> tags. However there are a number of SVG editors around that can be used to create and maintain complex shapes. Once a shape has been defined then you can scale it and apply styles.

You might like to copy and paste the following example paths and then maybe play around with the <div> dimensions, viewBox attributes and the style object.

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 510 60"};     let svg = createSvgElem("svg", svgAttr, null, div);     let svgStyle = {stroke: "red", "stroke-width": 1, fill: "transparent"};     svgAttr = {d: "m31.847256,20.147449c7.627459,-16.538212 37.512093,0 0,21.263415c-37.512093,-21.263415 \      -7.627459,-37.801627 0,-21.263415z"};     createSvgElem("path", svgAttr, svgStyle, svg);     svgAttr = {d: "m74.805598,52.920259c-1.325839,-1.078225 2.076708,-1.107086 \     1.542153,-2.302318c0.977352,-1.583842 3.518568,-2.542536 3.892802,-4.342332c-1.030665,-0.500215 \      -1.623434,-1.709964 0.342901,-1.566342c1.44199,-0.756148 1.261771,-2.303513 1.695423,-3.439055 \      c0.40623,-1.83279 0.498224,-3.687829 0.67271,-5.535677c-1.668471,-0.089279 -3.378213,-0.027091 \      -5.003929,-0.324922c0.046763,-1.228895 3.354712,-0.757366 3.50527,-1.916973c2.40519,-0.030278 \      0.133222,-2.53291 -0.84626,-3.129996c-1.253728,-1.007249 -2.088776,-2.597297 -1.135676,-3.841233 \      c1.363243,-0.630993 4.445648,-0.246902 4.306324,-1.90514c-0.92712,-0.368159 -2.104238,-0.134364 \      -3.143361,-0.199472c0.063904,-0.773238 0.127806,-1.546474 0.19171,-2.319711c1.374225,-0.073638 \      2.748453,-0.147281 4.122678,-0.220925c-0.697538,-0.505036 -1.741481,-0.876715 -2.002715,-1.591114 \      c0.888562,-1.017564 3.03006,-0.927739 4.564204,-0.918118c1.491783,-0.071682 4.211677,0.621513 \      2.178285,1.738058c-0.164809,0.274194 -1.76877,0.955962 -0.777544,0.881636c1.269146,0 2.538289,0 3.807432,0 \      c-0.003647,0.755909 -0.032817,1.519331 0.222013,2.259079c-0.968779,0.50261 -4.110267,-0.42933 \      -3.215785,1.121467c1.00718,0.648374 2.549715,0.631263 3.827187,0.916809c0.745128,0.05186 1.337877,0.187126 \      1.06936,0.793888c0.140053,1.12655 -0.267946,2.259795 -1.379505,3.114961c-0.78685,0.892343 -2.554104,2.009454 \      -2.030939,3.04235c0.989808,-0.112243 1.784926,0.34051 0.994348,0.849666c0.811317,0.418481 3.668275,0.322648 \      3.050353,1.370331c-1.44748,0.333793 -3.002438,0.194731 -4.504179,0.228793c0.399856,2.859806 0.952729,5.719883 \      1.876543,8.520375c0.539773,0.621425 2.683955,1.099034 0.92425,1.909172c-0.656721,1.508686 1.44088,2.638331 \      2.758263,3.606642c1.448451,0.755733 0.745692,2.276334 2.794824,2.518852c1.613781,1.089034 -2.215606,1.06004 \      -3.198757,1.062415c-6.508579,0.04889 -13.024179,0.11729 -19.530211,-0.028817c-0.532596,-0.046826 -1.190603,-0.036114 \      -1.57017,-0.35235l-0.000002,0.000001z"};     createSvgElem("path", svgAttr, svgStyle, svg); } initialise();

I am sure that the second drawn object (a chess piece) underlines the suggestion that you might like to do an Internet search for an SVG editor when faced with some sorts of SVG drawing challenges.

A graduated fill would be fun although it is just a little bit more fiddly than the <canvas> equivalent. This is because when building SVG graphics we are actually constructing XML. With that in mind, the following should seem straightforward. I did edit the main.css file to increase the size of the dvScale <div> to create a bit more display space though. I went for 500px wide and 300px high and that will be reflected in the dimensions of the viewBox attribute.

The code comments document the steps.
var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 500 300"};     let svg = createSvgElem("svg", svgAttr, null, div);     // gradients are located in a "defs" element in the XML     let defs = createSvgElem("defs", null, null, svg);     // now we define the gradient attributes including an id     svgAttr = {x1: 0, x2: 0, y1: 0, y2: 1, id: "grad1"}; // vertical gradient     let grad = createSvgElem("linearGradient", svgAttr, null, defs);     // we now create 3 stops for the gradient     svgAttr = {offset: "0%", "stop-color": "red"};     createSvgElem("stop", svgAttr, null, grad);     svgAttr = {offset: "50%", "stop-color": "black", "stop-opacity": 0};     createSvgElem("stop", svgAttr, null, grad);     svgAttr = {offset: "100%", "stop-color": "blue"};     createSvgElem("stop", svgAttr, null, grad);     // now a rectangle to show the gradient off     svgAttr = {x: 20, y: 20, width: 100, height: 100, rx: 10, ry: 10};     createSvgElem("rect", svgAttr, {fill: "url(#grad1)"}, svg); // the style adds the gradient fill } initialise();

The result is quite pleasing:

There are also radial gradients with some additional features but the ability to create a pattern and use that as a fill is a standout feature.

var div = document.getElementById("dvScale"); function initialise(){     let svgAttr = {"viewBox": "0 0 500 300"};     let svg = createSvgElem("svg", svgAttr, null, div);     // patterns like gradients are inside a defs element     let defs = createSvgElem("defs", null, null, svg);     // now a horizontal gradient     svgAttr = {id: "grad1"};     let grad = createSvgElem("linearGradient", svgAttr, null, defs);     // with 2 stops     svgAttr = {offset: "10%", "stop-color": "white"};     createSvgElem("stop", svgAttr, null, grad);     svgAttr = {offset: "95%", "stop-color": "blue"};     createSvgElem("stop", svgAttr, null, grad);     // now we define the pattern     svgAttr = {x: 0, y: 0, width: 0.25, height: 0.25, id: "patt1"};     let patt = createSvgElem("pattern", svgAttr, null, defs);     // we can now create the components of the pattern     svgAttr = {x: 0, y: 0, width: 50, height: 50};     createSvgElem("rect", svgAttr, {fill: "cyan"}, patt); // a square     svgAttr = {cx: 25, cy: 25, r: 20}; // with a circle inside it     createSvgElem("circle", svgAttr, {fill: "url(#grad1)", "fill-opacity": 0.5}, patt);     // and a rectangle to show the repeating pattern off     svgAttr = {x: 20, y: 20, width: 200, height: 200, rx: 10, ry: 10};     createSvgElem("rect", svgAttr, {fill: "url(#patt1)"}, svg); } initialise();

The pattern repeats as it fills the rectangle. Patterns can be created using any combination of SVG drawing elements.

SVG graphics includes transformations and if you are mathematically inclined you can apply matrices but for the moment we could try a rotation. You will need to create a bit more screen space for this one but start by just changing the svgAttr object for the pattern filled rectangle to read:

svgAttr = {x: 120, y: 0, width: 200, height: 200, rx: 10, ry: 10, transform: "rotate(30)"};

Not bad for just a few lines of code.

A prowl around the Mozilla web site will reveal many other SVG features but I hope that this two part introduction has shown how to apply at least the basics from JavaScript. I think though that we should end with an animation.

Edit the final rectangle attribute object as shown below and store a reference to the created "rect" in a variable. We need that as we are going to add an "animate" tag to the "rect". Then add the two extra lines that define an animation that repeatedly adjusts the radius applied to the corners over a 10 second time-frame. Enjoy.

svgAttr = {x: 120, y: 0, width: 200, height: 200, transform: "rotate(30)"}; let rect = createSvgElem("rect", svgAttr, {fill: "url(#patt1)"}, svg); svgAttr = {attributeName: "rx", values: "0;100;0", dur: "10s", repeatCount: "indefinite"}; createSvgElem("animate", svgAttr, null, rect);

<added 29/06/20>
This texture library for SVG looks interesting and could be just the thing for charts and diagrams.
</added>

Tuesday, 12 May 2020

Draw diagrams in VS Code

This is another great Visual Studio Code extension and it allows you to draw diagrams using Draw.io. This makes it easy to include a diagram in a project folder and maintain it in just the same way as you would create and maintain JavaScript, HTML or CSS files (or indeed any other language file) using VS Code.


The image shows the extension in action.

You can use the extensions feature of VS Code to download and install Draw.io integration. Search for "draw.io" and select the Henning Dieterichs offering from any presented.

Once installed, restart VS Code and you are ready to go. To create a diagram, start with a new file and then save it as an XML file but with the .drawio extension. [VS code might attach .xml onto the end but you can rename it directly in the VS Code explorer view.] Now if you click on your xml/drawio file the newly installed extension will load and you can start adding drawing objects.

There is more information to be found at the project GitHub repository.

Wednesday, 22 April 2020

Intersections

It is common when writing JavaScript games to need to calculate if a line will intersect with a given rectangle, circle or another line. In the chapter on Ray Casting in my book on learning JavaScript programming the code required to test for an intersection between a "ray" and a game world wall or sprite proved straightforward as the problem was deliberately simplified by the overall structure. In other projects some more general functions might prove helpful.

Interactive demo web page here

Demo code available for download here

The demo page features three intersection functions. All three functions are based upon Internet research and I am grateful for all of the very useful suggestions I found for each.

1. Line intersection with a rectangle

function lineIntersectsRect(lStart, lEnd, rect) { let minX = lStart.x; let maxX = lEnd.x; if(lEnd.x < lStart.x) { maxX = lStart.x; minX = lEnd.x; } maxX = Math.min(maxX, rect.x + rect.width); minX = Math.max(minX, rect.x); if(minX > maxX) {return false;} let minY = lStart.y; let maxY = lEnd.y; let dx = lEnd.x - lStart.x; if(dx) { // there is a horizontal slope let a = (lEnd.y - lStart.y)/dx; let b = lStart.y - a * lStart.x; minY = a * minX + b; maxY = a * maxX + b; } if(minY > maxY) { [minY, maxY] = [maxY, minY]; // ES6 Destructuring Assignment swaps values } if(Math.max(minY, rect.y) > Math.min(maxY, rect.y + rect.height)){ return false; } return true; }























2. Line intersection with a circle

function lineIntersectsCircle(lStart, lEnd, center, radius) { let dist; const dx1 = lEnd.x - lStart.x; const dy1 = lEnd.y - lStart.y; const dx2 = center.x - lStart.x; const dy2 = center.y - lStart.y; // get the unit distance along the line of the closest point to circle center const u = (dx2 * dx1 + dy2 * dy1) / (dy1 * dy1 + dx1 * dx1); // if the point is on the line segment get the distance squared to circle center if(u >= 0 && u <= 1){ dist = Math.pow((lStart.x + dx1 * u - center.x), 2) + Math.pow((lStart.y + dy1 * u - center.y), 2); } else { // use the u distance to determine which end is closest and get dist squared to circle dist = u < 0 ? Math.pow((lStart.x - center.x), 2) + Math.pow((lStart.y - center.y), 2) : Math.pow((lEnd.x - center.x), 2) + Math.pow((lEnd.y - center.y), 2); } return dist < Math.pow(radius, 2); }

















3. line intersection with a line

function lineIntersectsLine(p0, p1, p2, p3) { let dx1 = p1.x - p0.x; let dy1 = p1.y - p0.y; let dx2 = p3.x - p2.x; let dy2 = p3.y - p2.y; let d = dx1 * dy2 - dx2 * dy1; if(!d) { return false;} let dIsPositive = d > 0; let xd1 = p0.x - p2.x; let yd1 = p0.y - p2.y; let s_numer = dx1 * yd1 - dy1 * xd1; if((s_numer < 0) === dIsPositive) { return false; // watch logic = both true or both false returns false } let t_numer = dx2 * yd1 - dy2 * xd1; if((t_numer < 0) === dIsPositive) { return false; } if((s_numer > d) === dIsPositive || (t_numer > d) === dIsPositive) { return false; } return true; }