@@ -50,12 +50,12 @@ additional form fields. These are hooked up to update the canvas
5050drawing context's `fillStyle`, `strokeStyle`, and `lineWidth` whenever
5151they are changed.
5252
53- Loading an ((image)) into the program can be done in two ways. The
53+ You can load an ((image)) into the program in two ways. The
5454first uses a file field, where the user can select a file on their own
5555((file system)). The second asks for a ((URL)), and will fetch an
5656image from the web.
5757
58- (((saving)))Saving an image is done in a somewhat atypical way. The
58+ (((saving)))Images are saved in a somewhat atypical way. The
5959“save” ((link)) at the right-hand side points at the current image. It
6060can be followed, shared, or saved. I will explain how this is achieved
6161in a moment.
@@ -69,7 +69,7 @@ need to construct these somehow.
6969most obvious format for defining complex DOM structures. But
7070separating the program into a piece of HTML and a script is made
7171difficult by the fact that many of the DOM elements need event
72- handlers, or have to be touched by the script in some other way. Thus,
72+ handlers or have to be touched by the script in some other way. Thus,
7373our script would have to make lots of `querySelector` (or similar)
7474calls in order to find the DOM elements that it needs to act on.
7575
@@ -81,9 +81,9 @@ saw in link:13_dom.html#standard[Chapter 13], the built-in interface
8181for building up a DOM structure is horrendously verbose. If we are
8282going to do a lot of DOM construction, we need a helper function.
8383
84- (((elt function)))This function below is an extended version of the
84+ (((elt function)))This helper function is an extended version of the
8585`elt` function from link:13_dom.html#elt[Chapter 13]. It creates an
86- element with the given name and ((attribute))s, and appends all
86+ element with the given name and ((attribute))s and appends all
8787further arguments it gets as child nodes, automatically converting
8888strings to ((text node))s.
8989
@@ -117,7 +117,7 @@ code as long and dull as a corporate end-user agreement.
117117(((createPaint function)))(((controls object)))The core of our program
118118is the `createPaint` function, which appends the paint interface to
119119the DOM element it is given as argument. Because we want to build our
120- program piece by piece, we define an object `controls`, which will
120+ program piece by piece, we define an object called `controls`, which will
121121hold functions to initialize the various controls below the image.
122122
123123// include_code
@@ -141,23 +141,23 @@ function createPaint(parent) {
141141
142142(((fillStyle property)))(((lineWidth property)))(((canvas
143143property)))(((context)))Each control has access to the ((canvas))
144- drawing context ( and, through that context's `canvas` property, to the
145- `<canvas>` element itself) . Most of the program's state lives in this
144+ drawing context and, through that context's `canvas` property, to the
145+ `<canvas>` element itself. Most of the program's state lives in this
146146canvas—it contains the current picture as well as the selected color
147147(in its `fillStyle` property) and brush size (in its `lineWidth`
148148property).
149149
150150(((class attribute)))(((CSS)))We wrap the canvas and the controls in
151- `<div>` elements with classes to be able to add some styling—for
152- example the grey border around the picture.
151+ `<div>` elements with classes to be able to add some styling, such as
152+ the grey border around the picture.
153153
154154== Tool selection ==
155155
156156(((mouse)))(((tools object)))(((hard-coding)))(((select (HTML
157- tag))))The first control we add is the the `<select>` element that
157+ tag))))The first control we add is the `<select>` element that
158158allows the user to pick a drawing ((tool)). As with `controls`, we
159159will use an object to collect the various tools, so that we do not
160- have to hard-code them all in one place, and can add more tools later.
160+ have to hard-code them all in one place and can add more tools later.
161161This object associates the names of the tools with the function that
162162should be called when they are selected and the canvas is clicked.
163163
@@ -222,10 +222,7 @@ function relativePos(event, element) {
222222function)))Several of the drawing tools need to listen for
223223`"mousemove"` events as long as the mouse button is held down. The
224224`trackDrag` function takes care of the event registration and
225- unregistration for such situations. It takes two arguments, a function
226- to call for each `"mousemove"` event and a function to call when the
227- mouse button is released again. Either argument can be omitted when it
228- is not needed.
225+ unregistration for such situations.
229226
230227// include_code
231228
@@ -244,7 +241,12 @@ function trackDrag(onMove, onEnd) {
244241}
245242----
246243
247- (((line tool)))The “line” tool uses these two helpers to do actual
244+ This function takes two arguments. One is a function to call for each
245+ `"mousemove"` event, and the other is a function to call when the
246+ mouse button is released again. Either argument can be omitted when it
247+ is not needed.
248+
249+ (((line tool)))The line tool uses two helpers to do actual
248250drawing.
249251
250252// include_code
@@ -266,13 +268,13 @@ tools.Line = function(event, cx, onEnd) {
266268};
267269----
268270
269- (((path,canvas)))(((lineCap property)))(((line)))It starts by setting the drawing
270- context's `lineCap` property to `"round"`, which has the effect that
271- both ends of a stroked path will be round, rather than the square
272- form that is the default behavior. This is a trick to make sure that
273- multiple separate lines, drawn in response to separate events, do look
274- like a single, coherent line. With bigger line widths, you will see
275- gaps at corners if you don't set this property .
271+ (((path,canvas)))(((lineCap property)))(((line)))The function starts by setting the drawing
272+ context's `lineCap` property to `"round"`, which causes both ends of
273+ a stroked path to be round rather than the default square form.
274+ This is a trick to make sure that multiple separate lines, drawn in
275+ response to separate events, look like a single, coherent line. With
276+ bigger line widths, you will see gaps at corners if you use the default flat
277+ line caps .
276278
277279(((mousemove event)))(((strokeStyle property)))(((lineWidth
278280property)))Then, for every `"mousemove"` event that occurs as long as
@@ -303,19 +305,19 @@ tools.Erase = function(event, cx) {
303305(((globalCompositeOperation property)))(((compositing)))(((erase
304306tool)))The `globalCompositeOperation` property influences the way
305307drawing operations on a canvas influence the color of the pixels they
306- touch. By default it is `"source-over"`, which means that the drawn
307- color is overlaid on the existing color at that spot. If it is an
308- opaque ((color)) , it will simply replace the old color, but if it is
308+ touch. By default, the property's value is `"source-over"`, which means that the drawn
309+ color is overlaid on the existing color at that spot. If the ((color)) is
310+ opaque, it will simply replace the old color, but if it is
309311partially transparent, the two will be mixed.
310312
311313The “erase” tool sets `globalCompositeOperation` to
312314`"destination-out"`, which has the effect of erasing the pixels we
313315touch, making them transparent again.
314316
315- (((drawing)))That gives us two tools. The program so far is a working
316- paint program, allows us to draw black lines a single pixel wide (the
317+ (((drawing)))That gives us two tools, so at this point, we have created a working
318+ paint program. It allows us to draw black lines a single pixel wide (the
317319default `strokeStyle` and `lineWidth` for a canvas), and erase them
318- again.
320+ again. Admittedly, this makes it a rather limited paint program.
319321
320322== Color and brush size ==
321323
@@ -327,7 +329,7 @@ add controls for those two settings.
327329field)))(((email field)))(((number field)))(((compatibility)))In
328330link:18_forms.html#forms[Chapter 18], I discussed a number of
329331different form ((field))s. Color fields were not among those.
330- Traditionally, browsers do not have built-in support for color
332+ Traditionally, browsers don't have built-in support for color
331333pickers, but in the past years, a number of new form field types have
332334been ((standard))ized. One of those is `<input type="color">`. Others
333335include `"date"`, `"email"`, `"url"`, and `"number"`. Not all
@@ -386,36 +388,36 @@ controls.brushSize = function(cx) {
386388};
387389----
388390
389- (((lineWidth property)))It generates options from an array of brush
391+ (((lineWidth property)))The code generates options from an array of brush
390392sizes, and again ensures that the canvas’ `lineWidth` is updated when
391393a brush size is chosen.
392394
393395== Saving ==
394396
395397(((save link)))To explain the implementation of the “save” link, I
396398must first tell you about _((data URL))s_. A data ((URL)) is a URL
397- whose ((protocol)) is _data:_ . Unlike regular _http:_ and _https:_
398- URLs, data URLs do not point at a resource, but contain the entire
399+ with _data:_ as its ((protocol)). Unlike regular _http:_ and _https:_
400+ URLs, data URLs don't point at a resource, but rather contain the entire
399401((resource)) inside of them. This is a data URL containing a very
400402simple HTML document:
401403
402404----
403405data:text/html,<h1 style="color:red">Hello!</h1>
404406----
405407
406- Such URLs are useful for various things , such as including small
408+ Such URLs are useful for various tasks , such as including small
407409images directly in a ((style sheet)) file. They also allow us to link
408- to things that we created on the client side, in the browser, without
410+ to files that we created on the client side, in the browser, without
409411first moving them to some server.
410412
411413(((canvas)))(((toDataURL method)))(((optimization)))(((href
412- attribute)))Canvas elements have a convenient method `toDataURL`,
414+ attribute)))Canvas elements have a convenient method, called `toDataURL`,
413415which will return a data URL that contains the picture on the canvas
414416as an image file. We don't want to update our “save” link every time
415- the picture is changed, since for big pictures this involves moving
417+ the picture is changed, however. For big pictures, that involves moving
416418quite a lot of data into a link, and would be noticeably slow.
417419Instead, we rig the link to update its `href` attribute whenever it is
418- focused ( with the keyboard) or the mouse is moved over it.
420+ focused with the keyboard or the mouse is moved over it.
419421
420422// include_code
421423
@@ -452,12 +454,14 @@ should work well.
452454
453455(((security)))(((privacy)))(((cross-domain request)))But here we once
454456again run into the subtleties of browser ((sandbox))ing. When an
455- ((image)) is loaded from a URL on another ((domain)), and the server's
456- response did not include a header that tells the browser that this
457+ ((image)) is loaded from a URL on another ((domain)), if the server's
458+ response doesn't include a header that tells the browser the
457459resource may be used from other domains (see
458- link:17_http.html#http_sandbox[Chapter 17]), the ((canvas)) now
459- contains information that the _user_ may look at, but that the
460- _script_ may not. We may have requested a picture that contains
460+ link:17_http.html#http_sandbox[Chapter 17]), then the ((canvas)) will
461+ contain information that the _user_ may look at, but that the
462+ _script_ may not.
463+
464+ We may have requested a picture that contains
461465private information (for example, a graph showing the user's bank
462466account balance), using the user's ((session)). If scripts could get
463467information out of that picture, they could snoop on the user in
@@ -482,7 +486,7 @@ after the colon when they are followed, so that the link will show an
482486== Loading image files ==
483487
484488(((img (HTML tag))))(((load event)))(((file system)))The final two
485- controls are used to load images, from local files and from URLs.
489+ controls are used to load images from local files and from URLs.
486490We'll need the following helper function, which tries to load an image
487491file from a ((URL)) and replace the contents of the canvas with it.
488492
@@ -510,13 +514,13 @@ function loadImageURL(cx, url) {
510514size of the canvas to precisely fit the image. For some reason,
511515changing the size of a canvas will cause its drawing context to forget
512516configuration properties like `fillStyle` and `lineWidth`, so the
513- function first saves those, and restores them after it has updated the
517+ function first saves those. It restores them after it has updated the
514518size.
515519
516520(((FileReader type)))(((readAsDataURL method)))The control for loading
517521a local file uses the `FileReader` technique from
518522link:18_forms.html#filereader[Chapter 18]. Apart from the `readAsText`
519- method we used there, such reader objects also have a method
523+ method we used there, such reader objects also have a method called
520524`readAsDataURL`, which is exactly what we need here. We load the
521525((file)) that the user chose as a data URL, and pass it to
522526`loadImageURL` to put it into the canvas.
@@ -567,7 +571,7 @@ controls.openURL = function(cx) {
567571----
568572
569573We have now defined all the controls that our simple paint program
570- needs. But it could use a few more tools.
574+ needs, but it could still use a few more tools.
571575
572576== Finishing up ==
573577
@@ -589,16 +593,16 @@ tools.Text = function(event, cx) {
589593};
590594----
591595
592- It would be possible to add extra fields for the ((font)) size and the
593- font, but for simplicity's sake we always use a sans-serif font, and
594- base the font size on the current brush size, with a minimum size of 7
596+ You could add extra fields for the ((font)) size and the
597+ font, but for simplicity's sake, we always use a sans-serif font, and
598+ base the font size on the current brush size. The minimum size is seven
595599pixels, because text smaller than that is unreadable.
596600
597601(((spray paint tool)))(((random number)))Another indispensable tool
598602for drawing amateurish computer graphics is the “spray paint” tool.
599603This one draws dots in random locations under the ((brush)) as long as
600- the mouse is held down, allowing denser or less dense speckling to be
601- created by moving the mouse faster or slower .
604+ the mouse is held down, creating denser or less dense speckling
605+ based on how fast or slow the mouse moves .
602606
603607// include_code
604608
@@ -626,16 +630,16 @@ tools.Spray = function(event, cx) {
626630};
627631----
628632
629- (((setInterval function)))(((trackDrag function)))The tool uses
633+ (((setInterval function)))(((trackDrag function)))The spray tool uses
630634`setInterval` to spit out colored dots every 25 milliseconds as long
631635as the mouse button is held down. The `trackDrag` function is used to
632636keep `currentPos` pointing at the current mouse position, and to turn
633637off the interval when the mouse button is released.
634638
635639To determine how many dots to draw every time the interval fires, the
636- function computes the ((area)) of the current brush, and divides that
637- by 30. To find a random position under the brush, the following
638- function is used:
640+ function computes the ((area)) of the current brush and divides that
641+ by 30. To find a random position under the brush, the `randomPointInRadius`
642+ function is used.
639643
640644// include_code
641645
@@ -652,18 +656,17 @@ function randomPointInRadius(radius) {
652656}
653657----
654658
655- (((Pythagoras)))This generates points in the square between (-1,-1)
656- and (1,1), and as soon as it finds one that lies within a ((circle))
657- with ((radius)) 1 (using the Pythagorean theorem) , it returns it,
659+ (((Pythagoras)))This function generates points in the square between (-1,-1)
660+ and (1,1). Using the Pythagorean theorem, it tests whether the generated point lies within a ((circle))
661+ of ((radius)) one. As soon as the function finds such a point , it returns the point
658662multiplied by the `radius` argument.
659663
660664(((uniformity)))(((Math.sin function)))(((Math.cos function)))The loop
661- is necessary because the straightforward way of generating a random
662- point within a circle, by using a random ((angle)) and distance, and
663- calling `Math.sin` and `Math.cos` to create the corresponding point,
664- the dots are not distributed uniformly, but more likely to appear near
665- the center of the circle. There are ways around this, but they are
666- more complicated than the loop above.
665+ is necessary for a uniform distribution of dots. The straightforward way
666+ of generating a random point within a circle would be to use a random ((angle)) and distance and
667+ call `Math.sin` and `Math.cos` to create the corresponding point. But with that method,
668+ the dots are more likely to appear near the center of the circle.
669+ There are other ways around that, but they're more complicated than the loop above.
667670
668671We now have a functioning paint program.(!interactive Run the code below to
669672try it out.!)
@@ -851,7 +854,7 @@ endif::interactive_target[]
851854
852855(((color picker (exercise))))(((relativePos function)))(((rgb
853856(CSS))))You'll again need to use `relativePos` to find out which
854- pixel. The `pixelAt` function in the example demonstrates how to get
857+ pixel was clicked . The `pixelAt` function in the example demonstrates how to get
855858the values for a given pixel. Putting those into an `rgb` string
856859merely requires some string ((concatenation)).
857860
@@ -864,7 +867,7 @@ that you don't accidentally handle the wrong kind of exception.
864867=== Flood fill ===
865868
866869(((flood fill (exercise))))This is a more advanced exercise than the
867- preceding two, and will require you to design a non-trivial solution
870+ preceding two, and it will require you to design a non-trivial solution
868871to a tricky problem. Make sure you have plenty of time and
869872((patience)) before starting to work on this exercise, and do not get
870873discouraged by initial failures.
@@ -874,7 +877,7 @@ and the whole group of pixels around it that have the same color. For
874877the purpose of this exercise, we will consider such a group to include
875878all pixels that can be reached from our starting pixel by moving in
876879single-pixel horizontal and vertical steps (not diagonal), without
877- ever touching a pixel that has a color different form the starting
880+ ever touching a pixel that has a color different from the starting
878881pixel.
879882
880883The picture below illustrates the set of pixels colored when the flood
@@ -907,10 +910,10 @@ have to do something similar to the backtracking done by the regular
907910expression matcher, described in
908911link:09_regexp.html#backtracking[Chapter 9]. Whenever more than one
909912possible direction to proceed is seen, you must store all the
910- directions you do not take immediately, and look at them later, when
913+ directions you do not take immediately and look at them later, when
911914you finish your current walk.
912915
913- (((performance)))(((optimization)))In a normal-sized picture there are
916+ (((performance)))(((optimization)))In a normal-sized picture, there are
914917a _lot_ of pixels. Thus, you must take care to do the minimal amount
915918of work required, or your program will take a very long to run. For
916919example, every walk must ignore pixels seen by previous walks, so that
@@ -973,11 +976,11 @@ structure that tracks colored pixels will be consulted _very_ often.
973976Searching through the whole thing every time a new pixel is visited
974977will take a lot of time. You could instead create an array that has a
975978value in it for every pixel, using again the x + y × width scheme for
976- associating positions with pixels, and when checking if a pixel has
977- been colored already, directly access the field corresponding to the
979+ associating positions with pixels. When checking if a pixel has
980+ been colored already, you could directly access the field corresponding to the
978981current pixel.
979982
980- (((comparison,of colors)))Comparing colors can be done by running over
983+ (((comparison,of colors)))You can compare colors by running over
981984the relevant part of the data array, comparing one field at a time. Or
982985you can “condense” a color down to a single number or string, and
983986compare those. When doing this, ensure that every color produces a
0 commit comments