Skip to content

Commit e91b8c8

Browse files
committed
Integrate editing for chapter 19
1 parent 9ab7ee1 commit e91b8c8

File tree

1 file changed

+77
-74
lines changed

1 file changed

+77
-74
lines changed

19_paint.txt

Lines changed: 77 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ additional form fields. These are hooked up to update the canvas
5050
drawing context's `fillStyle`, `strokeStyle`, and `lineWidth` whenever
5151
they 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
5454
first 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
5656
image 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
6060
can be followed, shared, or saved. I will explain how this is achieved
6161
in a moment.
@@ -69,7 +69,7 @@ need to construct these somehow.
6969
most obvious format for defining complex DOM structures. But
7070
separating the program into a piece of HTML and a script is made
7171
difficult 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,
7373
our script would have to make lots of `querySelector` (or similar)
7474
calls 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
8181
for building up a DOM structure is horrendously verbose. If we are
8282
going 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
8787
further arguments it gets as child nodes, automatically converting
8888
strings 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
118118
is the `createPaint` function, which appends the paint interface to
119119
the 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
121121
hold 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
143143
property)))(((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
146146
canvas—it contains the current picture as well as the selected color
147147
(in its `fillStyle` property) and brush size (in its `lineWidth`
148148
property).
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
158158
allows the user to pick a drawing ((tool)). As with `controls`, we
159159
will 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.
161161
This object associates the names of the tools with the function that
162162
should be called when they are selected and the canvas is clicked.
163163

@@ -222,10 +222,7 @@ function relativePos(event, element) {
222222
function)))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
248250
drawing.
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
278280
property)))Then, for every `"mousemove"` event that occurs as long as
@@ -303,19 +305,19 @@ tools.Erase = function(event, cx) {
303305
(((globalCompositeOperation property)))(((compositing)))(((erase
304306
tool)))The `globalCompositeOperation` property influences the way
305307
drawing 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
309311
partially transparent, the two will be mixed.
310312

311313
The “erase” tool sets `globalCompositeOperation` to
312314
`"destination-out"`, which has the effect of erasing the pixels we
313315
touch, 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
317319
default `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.
327329
field)))(((email field)))(((number field)))(((compatibility)))In
328330
link:18_forms.html#forms[Chapter 18], I discussed a number of
329331
different 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
331333
pickers, but in the past years, a number of new form field types have
332334
been ((standard))ized. One of those is `<input type="color">`. Others
333335
include `"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
390392
sizes, and again ensures that the canvas’ `lineWidth` is updated when
391393
a brush size is chosen.
392394

393395
== Saving ==
394396

395397
(((save link)))To explain the implementation of the “save” link, I
396398
must 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
400402
simple HTML document:
401403

402404
----
403405
data: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
407409
images 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
409411
first 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`,
413415
which will return a data URL that contains the picture on the canvas
414416
as 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
416418
quite a lot of data into a link, and would be noticeably slow.
417419
Instead, 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
454456
again 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
457459
resource 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
461465
private information (for example, a graph showing the user's bank
462466
account balance), using the user's ((session)). If scripts could get
463467
information 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.
486490
We'll need the following helper function, which tries to load an image
487491
file from a ((URL)) and replace the contents of the canvas with it.
488492

@@ -510,13 +514,13 @@ function loadImageURL(cx, url) {
510514
size of the canvas to precisely fit the image. For some reason,
511515
changing the size of a canvas will cause its drawing context to forget
512516
configuration 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
514518
size.
515519

516520
(((FileReader type)))(((readAsDataURL method)))The control for loading
517521
a local file uses the `FileReader` technique from
518522
link: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

569573
We 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
595599
pixels, because text smaller than that is unreadable.
596600

597601
(((spray paint tool)))(((random number)))Another indispensable tool
598602
for drawing amateurish computer graphics is the “spray paint” tool.
599603
This 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
631635
as the mouse button is held down. The `trackDrag` function is used to
632636
keep `currentPos` pointing at the current mouse position, and to turn
633637
off the interval when the mouse button is released.
634638

635639
To 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
658662
multiplied 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

668671
We now have a functioning paint program.(!interactive Run the code below to
669672
try 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
855858
the values for a given pixel. Putting those into an `rgb` string
856859
merely 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
868871
to a tricky problem. Make sure you have plenty of time and
869872
((patience)) before starting to work on this exercise, and do not get
870873
discouraged by initial failures.
@@ -874,7 +877,7 @@ and the whole group of pixels around it that have the same color. For
874877
the purpose of this exercise, we will consider such a group to include
875878
all pixels that can be reached from our starting pixel by moving in
876879
single-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
878881
pixel.
879882

880883
The 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
907910
expression matcher, described in
908911
link:09_regexp.html#backtracking[Chapter 9]. Whenever more than one
909912
possible 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
911914
you 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
914917
a _lot_ of pixels. Thus, you must take care to do the minimal amount
915918
of work required, or your program will take a very long to run. For
916919
example, 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.
973976
Searching through the whole thing every time a new pixel is visited
974977
will take a lot of time. You could instead create an array that has a
975978
value 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
978981
current 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
981984
the relevant part of the data array, comparing one field at a time. Or
982985
you can “condense” a color down to a single number or string, and
983986
compare those. When doing this, ensure that every color produces a

0 commit comments

Comments
 (0)