-
Notifications
You must be signed in to change notification settings - Fork 3
/
Pen.pck.st
644 lines (568 loc) · 22.2 KB
/
Pen.pck.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
'From Cuis 5.0 [latest update: #4219] on 12 June 2020 at 11:58:06 am'!
'Description Draw with a pen and messages as go: turn: etc.'!
!provides: 'Pen' 1 5!
SystemOrganization addCategory: #Pen!
!classDefinition: #Pen category: #Pen!
BitBlt subclass: #Pen
instanceVariableNames: 'location direction penDown'
classVariableNames: ''
poolDictionaries: ''
category: 'Pen'!
!classDefinition: 'Pen class' category: #Pen!
Pen class
instanceVariableNames: ''!
!classDefinition: #PenPointRecorder category: #Pen!
Pen subclass: #PenPointRecorder
instanceVariableNames: 'points'
classVariableNames: ''
poolDictionaries: ''
category: 'Pen'!
!classDefinition: 'PenPointRecorder class' category: #Pen!
PenPointRecorder class
instanceVariableNames: ''!
!Pen commentStamp: '<historical>' prior: 0!
My instances can scribble on the screen or some other Form, drawing and printing at any angle. Since I am a BitBlt, the drawing can be done with an arbitary source Form.
!
!PenPointRecorder commentStamp: 'nice 3/24/2010 07:38' prior: 0!
This class is a special kind of Pen that instead of actually drawing lines records the destination points for those lines. These points can later be accessed through my accessing method #points.
This can be useful when determining the boundaries of a drawing session.
Example:
| pen |
pen := PenPointRecorder new.
pen up; goto: 100@100; down; goto: 120@120.
Transcript cr;
show: 'Bounding box for drawing: ';
show: (Rectangle encompassing: pen points)
Implementation note: Shouldn't we override #drawFrom:to:withFirstPoint: instead, and what about #drawLoopX:Y:? Aren't we missing those calls?!
!Pen methodsFor: 'operations' stamp: 'jm 4/28/1998 03:40'!
color: aColorOrInteger
"Set the pen to the given color or to a color chosen from a fixed set of colors."
| count c |
aColorOrInteger isInteger
ifTrue: [
destForm depth = 1 ifTrue: [^ self fillColor: Color black].
count _ 19. "number of colors in color wheel"
c _ (Color red wheel: count) at: ((aColorOrInteger * 7) \\ count) + 1]
ifFalse: [c _ aColorOrInteger]. "assume aColorOrInteger is a Color"
self fillColor: c.
! !
!Pen methodsFor: 'operations'!
down
"Set the state of the receiver's pen to down (drawing)."
penDown _ true! !
!Pen methodsFor: 'operations' stamp: 'HilaireFernandes 11/8/2017 12:11:52'!
fill: drawBlock color: color
| region tileForm tilePen shape saveColor recorder |
drawBlock value: (recorder := self as: PenPointRecorder).
region := Rectangle encompassing: recorder points.
tileForm := Form extent: region extent+6.
tilePen := Pen newOnForm: tileForm.
tilePen location: location-(region origin-3)
direction: direction
penDown: penDown.
drawBlock value: tilePen. "Draw the shape in B/W"
saveColor := halftoneForm.
drawBlock value: self.
halftoneForm := saveColor.
shape := (tileForm findShapeAroundSeedBlock: [:f |
f border: (0@0 corner: f extent) width: 1]) reverse.
shape copy: shape boundingBox from: tileForm to: 0@0 rule: Form erase.
destForm fillShape: shape fillColor: color at: region origin-3! !
!Pen methodsFor: 'operations'!
go: distance
"Move the pen in its current direction a number of bits equal to the
argument, distance. If the pen is down, a line will be drawn using the
receiver's form source as the shape of the drawing brush."
self goto: (direction degreeCos @ direction degreeSin) * distance + location! !
!Pen methodsFor: 'operations' stamp: 'di 11/4/97 20:11'!
goto: aPoint
"Move the receiver to position aPoint. If the pen is down, a line will be
drawn from the current position to the new one using the receiver's
form source as the shape of the drawing brush. The receiver's set
direction does not change."
| old |
old _ location.
location _ aPoint.
penDown ifTrue: [self drawFrom: old rounded
to: location rounded]
"NOTE: This should be changed so it does NOT draw the first point, so as
not to overstrike at line junctions. At the same time, place should draw
a single dot if the pen is down, as should down (put-pen-down) if it
was not down before."! !
!Pen methodsFor: 'operations'!
home
"Place the receiver at the center of its frame."
location _ destForm boundingBox center! !
!Pen methodsFor: 'operations'!
north
"Set the receiver's direction to facing toward the top of the display screen."
direction _ 270! !
!Pen methodsFor: 'operations'!
place: aPoint
"Set the receiver at position aPoint. No lines are drawn."
location _ aPoint! !
!Pen methodsFor: 'operations' stamp: 'jmv 6/11/2020 16:50:10'!
print: str withFont: font
"Print the given string in the given font at the current heading"
| lineStart scale wasDown |
scale := sourceForm width.
wasDown := penDown.
lineStart := location.
str do:
[:char |
char = Character cr ifTrue:
[self place: lineStart; up; turn: 90; go: font lineSpacing*scale; turn: -90; down]
ifFalse:
[ | charStart pix rowStart form backgroundCode |
form := font glyphAt: char.
backgroundCode := 1<< (form depth // 3 * 3) - 1.
charStart := location.
wasDown ifTrue: [
self up; turn: -90; go: font descent*scale; turn: 90; down.
0 to: form height-1 do:
[:y |
rowStart := location.
pix := RunArray newFrom:
((0 to: form width-1) collect: [:x | form pixelValueAt: x@y]).
pix runs with: pix values do:
[:run :value |
value = backgroundCode
ifTrue: [self up; go: run*scale; down]
ifFalse: [self go: run*scale]].
self place: rowStart; up; turn: 90; go: scale; turn: -90; down].
].
self place: charStart; up; go: form width*scale; down].
].
wasDown ifFalse: [self up]
"
Display restoreAfter:
[Pen new squareNib: 2; color: Color red; turn: 45;
print: 'The owl and the pussycat went to sea
in a beautiful pea green boat.' withFont: FontFamily defaultFamilyAndPointSize ]
"! !
!Pen methodsFor: 'operations'!
turn: degrees
"Change the direction that the receiver faces by an amount equal to the
argument, degrees."
direction _ direction + degrees! !
!Pen methodsFor: 'operations'!
up
"Set the state of the receiver's pen to up (no drawing)."
penDown _ false! !
!Pen methodsFor: 'geometric designs' stamp: 'di 6/11/1998 22:01'!
dragon: n "Display restoreAfter: [Display fillWhite. Pen new dragon: 10]."
"Display restoreAfter: [Display fillWhite. 1 to: 4 do:
[:i | Pen new color: i; turn: 90*i; dragon: 10]]"
"Draw a dragon curve of order n in the center of the screen."
n = 0
ifTrue: [self go: 5]
ifFalse: [n > 0
ifTrue: [self dragon: n - 1; turn: 90; dragon: 1 - n]
ifFalse: [self dragon: -1 - n; turn: -90; dragon: 1 + n]]
! !
!Pen methodsFor: 'geometric designs' stamp: 'di 6/14/1998 13:42'!
filberts: n side: s "Display restoreAfter: [Pen new filberts: 4 side: 5]"
"Two Hilbert curve fragments form a Hilbert tile. Draw four interlocking
tiles of order n and sides length s."
| n2 |
Display fillWhite.
n2 _ 1 bitShift: n - 1.
self up; go: 0 - n2 * s; down.
1 to: 4 do:
[:i |
self fill: [:p |
p hilbert: n side: s.
p go: s.
p hilbert: n side: s.
p go: s.
p up.
p go: n2 - 1 * s.
p turn: -90.
p go: n2 * s.
p turn: 180.
p down]
color: (Color perform: (#(yellow red green blue) at: i))]! !
!Pen methodsFor: 'geometric designs'!
hilbert: n side: s
"Draw an nth level Hilbert curve with side length s in the center of the
screen. Write directly into the display's bitmap only. A Hilbert curve is
a space-filling curve."
| a m |
n = 0 ifTrue: [^self turn: 180].
n > 0
ifTrue:
[a _ 90.
m _ n - 1]
ifFalse:
[a _ -90.
m _ n + 1].
self turn: a.
self hilbert: 0 - m side: s.
self turn: a; go: s.
self hilbert: m side: s.
self turn: 0 - a; go: s; turn: 0 - a.
self hilbert: m side: s.
self go: s; turn: a.
self hilbert: 0 - m side: s.
self turn: a
"
(Pen new) hilbert: 3 side: 8.
(Pen new sourceForm: Cursor wait) combinationRule: Form under;
hilbert: 3 side: 25.
"! !
!Pen methodsFor: 'geometric designs'!
hilberts: n "Display restoreAfter: [Display fillWhite. Pen new hilberts: 5]"
"Draws n levels of nested Hilbert curves"
| s |
self up; turn: 90; go: 128; down.
1 to: n do:
[:i |
s _ 256 bitShift: 0 - i.
self defaultNib: n - i * 2 + 1.
self color: i+1.
self up; go: 0 - s / 2; turn: -90; go: s / 2; turn: 90; down.
self hilbert: i side: s.
self go: s.
self hilbert: i side: s.
self go: s]! !
!Pen methodsFor: 'geometric designs' stamp: 'HilaireFernandes 11/8/2017 15:39:15'!
mandala: npoints
"Display restoreAfter: [Pen new mandala: 30]"
"On a circle of diameter d, place npoints number of points. Draw all possible connecting lines between the circumferential points."
| l points d |
Display fillWhite.
d := Display height-50.
l := 3.14 * d / npoints.
self home; up; turn: -90; go: d // 2; turn: 90; go: 0 - l / 2; down.
points := Array new: npoints.
1 to: npoints do: [:i |
points at: i put: location rounded.
self go: l; turn: 360.0 / npoints].
npoints // 2 to: 1 by: -1 do: [:i |
self color: i.
1 to: npoints do: [:j |
self place: (points at: j).
self goto: (points at: j + i - 1 \\ npoints + 1)]].
ImageMorph new image: Display copy; openInWorld! !
!Pen methodsFor: 'geometric designs' stamp: 'jm 5/6/1998 22:26'!
spiral: n angle: a
"Draw a double squiral (see Papert, MindStorms), where each design is made
by moving the receiver a distance of n after turning the amount + or -a."
1 to: n do:
[:i |
self color: i * 2.
self go: i; turn: a]
"
Display restoreAfter: [
Display fillWhite. Pen new spiral: 200 angle: 89; home; spiral: 200 angle: -89].
"! !
!Pen methodsFor: 'geometric designs' stamp: 'HilaireFernandes 11/8/2017 11:37:38'!
web "Display restoreAfter: [Pen new web]"
"Draw pretty web-like patterns from the mouse movement on the screen.
Press the mouse button to draw, option-click to exit.
By Dan Ingalls and Mark Lentczner. "
| history newPoint ancientPoint lastPoint filter color |
"self erase."
color := 1.
[ history := OrderedCollection new.
Sensor waitButton.
Sensor isMouseButton2Pressed ifTrue: [^ self].
filter := lastPoint := Sensor mousePoint.
20 timesRepeat: [ history addLast: lastPoint ].
self color: (color := color + 1).
[ Sensor isMouseButton1Pressed ] whileTrue:
[ newPoint := Sensor mousePoint.
(newPoint = lastPoint) ifFalse:
[ ancientPoint := history removeFirst.
filter := filter * 4 + newPoint // 5.
self place: filter.
self goto: ancientPoint.
lastPoint := newPoint.
history addLast: filter ] ] ] repeat! !
!Pen methodsFor: 'private' stamp: 'jmv 7/27/2015 17:01'!
copyBits
super copyBits.
destForm == Display ifTrue: [
DisplayScreen screenUpdateRequired: self clipRect ]! !
!Pen methodsFor: 'private' stamp: 'jmv 7/27/2015 17:01'!
drawLoopX: xDelta Y: yDelta
super drawLoopX: xDelta Y: yDelta.
destForm == Display ifTrue: [
DisplayScreen screenUpdateRequired: self clipRect ]! !
!Pen methodsFor: 'private' stamp: 'di 6/11/1998 16:09'!
location: aPoint direction: aFloat penDown: aBoolean
location _ aPoint.
direction _ aFloat.
penDown _ aBoolean! !
!Pen methodsFor: 'private'!
sourceForm: aForm
(aForm depth = 1 and: [destForm depth > 1])
ifTrue: ["Map 1-bit source to all ones for color mask"
colorMap _ Bitmap with: 0 with: 16rFFFFFFFF]
ifFalse: [colorMap _ nil].
^ super sourceForm: aForm! !
!Pen methodsFor: 'initialization' stamp: 'jm 4/28/1998 04:02'!
defaultNib: widthInteger
"Nib is the tip of a pen. This sets up the pen, with a nib of width widthInteger. You can also set the shape of the pen nib using:
roundNib: widthInteger, or
squareNib: widthInteger, or
sourceForm: aForm"
"Example:
| bic |
bic _ Pen new sourceForm: Cursor normal.
bic combinationRule: Form paint; turn: 90.
10 timesRepeat: [bic down; go: 3; up; go: 10]."
self color: Color black.
self squareNib: widthInteger.
! !
!Pen methodsFor: 'initialization' stamp: 'jm 4/28/1998 04:03'!
roundNib: diameter
"Makes this pen draw with a round dot of the given diameter."
self sourceForm: (Form dotOfSize: diameter).
combinationRule _ Form paint.
! !
!Pen methodsFor: 'initialization' stamp: 'jm 4/28/1998 04:03'!
squareNib: widthInteger
"Makes this pen draw with a square nib of the given width."
self sourceForm: (Form extent: widthInteger @widthInteger) fillBlack.
self combinationRule: Form over. "a bit faster than paint mode"
! !
!Pen methodsFor: 'accessing'!
direction
"Answer the receiver's current direction. 0 is towards the top of the
screen."
^direction! !
!Pen methodsFor: 'accessing'!
location
"Answer where the receiver is currently located."
^location! !
!Pen class methodsFor: 'examples' stamp: 'jm 5/6/1998 22:28'!
example
"Draw a spiral with a pen that is 2 pixels wide."
"Display restoreAfter: [Pen example]"
| bic |
bic _ self new.
bic defaultNib: 2.
bic color: Color blue.
bic combinationRule: Form over.
1 to: 100 do: [:i | bic go: i*4. bic turn: 89].
! !
!Pen class methodsFor: 'examples' stamp: 'jmv 1/25/2016 12:21'!
exampleSketch
"This is a simple drawing algorithm to get a sketch on the display screen.
Draws whenever mouse button down. Ends with option-click."
| aPen color |
aPen _ Pen new.
color _ 0.
[Sensor isMouseButton2Pressed]
whileFalse:
[aPen place: Sensor mousePoint; color: (color _ color + 1).
[Sensor isMouseButton1Pressed]
whileTrue: [aPen goto: Sensor mousePoint]].
Sensor waitNoButton.
"
Pen exampleSketch
"! !
!Pen class methodsFor: 'examples' stamp: 'jmv 1/25/2016 12:20'!
makeStar
| sampleForm pen |
sampleForm _ Form extent: 50@50. "Make a form"
pen _ Pen newOnForm: sampleForm.
pen place: 24@50; turn: 18. "Draw a 5-pointed star on it."
1 to: 5 do: [:i | pen go: 19; turn: 72; go: 19; turn: -144].
^ sampleForm
"
Pen makeStar display
"! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jmv 5/14/2018 12:46:24'!
feltTip: width cellSize: cellSize
"Warning: This example potentially uses a large amount of memory--it creates a Form with cellSize squared bits for every Display pixel."
"In this example, all drawing is done into a large, monochrome Form and then scaled down onto the Display using smoothing. The larger the cell size, the more possible shades of gray can be generated, and the smoother the resulting line appears. A cell size of 8 yields 64 possible grays, while a cell size of 16 gives 256 levels, which is about the maximum number of grays that the human visual system can distinguish. The width parameter determines the maximum line thickness. Requires the optional tablet support primitives which may not be supported on all platforms. Works best in full screen mode. Shift-mouse to exit."
"
Pen feltTip: 2.7 cellSize: 8
"
| tabletScale bitForm pen warp |
tabletScale := self tabletScaleFactor.
bitForm := Form extent: Display extent * cellSize depth: 1.
pen := Pen newOnForm: bitForm.
pen color: Color black.
warp := (WarpBlt toForm: Display)
sourceForm: bitForm;
colorMap: (bitForm colormapIfNeededFor: Display);
cellSize: cellSize;
combinationRule: Form over.
Display fillColor: Color white.
Display restoreAfter: [ | p r nibSize srcR startP dstR |
[Sensor shiftPressed and: [Sensor isAnyButtonPressed]] whileFalse: [
p := (Sensor tabletPoint * cellSize * tabletScale) rounded.
nibSize := (Sensor tabletPressure * (cellSize * width)) rounded.
nibSize > 0
ifTrue: [
pen squareNib: nibSize.
startP := pen location.
pen goto: p.
r := startP rect: pen location.
dstR := (r origin // cellSize) corner: ((r corner + nibSize + (cellSize - 1)) // cellSize).
srcR := (dstR origin * cellSize) corner: (dstR corner * cellSize).
warp copyQuad: srcR innerCorners toRect: dstR]
ifFalse: [
pen place: p]]].
! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jmv 5/14/2018 12:41:52'!
inkBrush
"Similar to simplePressurePen, but this example uses the average of the recent pen pressure values. The effect is that of a Japanese ink brush that comes up gradually off the paper as the brush is lifted, causing end (and beginning) of each stroke to taper. Requires the optional tablet support primitives which may not be supported on all platforms. Works best in full screen mode. Shift-mouse to exit."
"
Pen inkBrush
"
| tabletScale historyMSecs pressureHistory pen |
tabletScale _ self tabletScaleFactor.
historyMSecs _ 120.
pressureHistory _ OrderedCollection new.
pen _ Pen newOnForm: Display.
pen color: Color black.
Display fillColor: Color white.
Display restoreAfter: [ | sum p averagePressure now currentPressure |
[Sensor shiftPressed and: [Sensor isAnyButtonPressed]] whileFalse: [
"compute the average pressure over last historyMSecs milliseconds"
now _ Time localMillisecondClock.
currentPressure _ (20.0 * Sensor tabletPressure) rounded.
pressureHistory addLast: (Array with: now with: currentPressure).
[pressureHistory size > 0 and:
[(pressureHistory first first + historyMSecs) < now]]
whileTrue: [pressureHistory removeFirst]. "prune old entries"
sum _ pressureHistory inject: 0 into: [:t :e | t + e last].
averagePressure _ sum // pressureHistory size.
p _ (Sensor tabletPoint * tabletScale) rounded.
averagePressure > 0
ifTrue: [
pen roundNib: averagePressure.
pen goto: p]
ifFalse: [
pen place: p]]].
! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jmv 5/14/2018 12:42:28'!
simplePressurePen
"An example of using a pressure sensitive pen to control the thickness of the pen. This requires the optional tablet support primitives which may not be supported on all platforms. Works best in full screen mode. Shift-mouse to exit."
"
Pen simplePressurePen
"
| tabletScale pen |
tabletScale _ self tabletScaleFactor.
pen _ Pen newOnForm: Display.
pen color: Color black.
Display fillColor: Color white.
Display restoreAfter: [ | p pressure |
[Sensor shiftPressed and: [Sensor isAnyButtonPressed]] whileFalse: [
p _ (Sensor tabletPoint * tabletScale) rounded.
pressure _ (15.0 * Sensor tabletPressure) rounded.
pressure > 0
ifTrue: [
pen roundNib: pressure.
pen goto: p]
ifFalse: [
pen place: p]]].
! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jm 4/13/1999 11:12'!
tabletScaleFactor
"Answer a Point that scales tablet coordinates to Display coordinates, where the full extent of the tablet maps to the extent of the entire Display."
| tabletExtent |
tabletExtent _ Sensor tabletExtent.
^ (Display width asFloat / tabletExtent x) @ (Display height asFloat / tabletExtent y)
! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jmv 5/14/2018 12:43:07'!
testMouseTracking
"A very simple example of drawing using the mouse. Compare the tracking speed of this example with that of testTabletTracking. Mouse down to draw a stroke, shift-mouse to exit."
"
Pen testMouseTracking
"
| pen |
pen _ Pen newOnForm: Display.
pen roundNib: 8.
pen color: Color black.
Display fillColor: Color white.
Display restoreAfter: [ | p |
[Sensor shiftPressed and: [Sensor isAnyButtonPressed]] whileFalse: [
p _ Sensor mousePoint.
Sensor isAnyButtonPressed
ifTrue: [pen goto: p]
ifFalse: [
pen color: Color random.
pen place: p]]].
! !
!Pen class methodsFor: 'tablet drawing examples' stamp: 'jmv 5/14/2018 12:43:57'!
testTabletTracking
"A very simple example of drawing using the pen of a digitizing tablet such as a Wacom ArtZ tablet. This requires the optional tablet support primitives which may not be supported on all platforms. Compare the tracking speed of this example with that of testMouseTracking. On a Macintosh, the tablet primitives provide roughly 120 samples/second versus only 60 mouse samples/second, and the difference is noticable. Works best in full screen mode. Mouse down to draw a stroke, shift-mouse to exit."
"
Pen testTabletTracking
"
| tabletScale pen |
tabletScale _ self tabletScaleFactor.
pen _ Pen newOnForm: Display.
pen roundNib: 8.
pen color: Color black.
Display fillColor: Color white.
Display restoreAfter: [ | p |
[Sensor shiftPressed and: [Sensor isAnyButtonPressed]] whileFalse: [
p _ (Sensor tabletPoint * tabletScale) rounded.
Sensor tabletPressure > 0
ifTrue: [pen goto: p]
ifFalse: [
pen color: Color random.
pen place: p]]].
! !
!Pen class methodsFor: 'instance creation'!
new
^ self newOnForm: Display! !
!Pen class methodsFor: 'instance creation'!
newOnForm: aForm
| pen |
pen _ super new.
pen setDestForm: aForm.
pen sourceOrigin: 0@0.
pen home.
pen defaultNib: 1.
pen north.
pen down.
^ pen! !
!PenPointRecorder methodsFor: 'line drawing' stamp: 'md 11/14/2003 16:56'!
drawFrom: p1 to: p2
"Overridden to skip drawing but track bounds of the region traversed."
points ifNil: [points := OrderedCollection with: p1].
points addLast: p2! !
!PenPointRecorder methodsFor: 'accessing' stamp: 'di 6/21/1998 09:35'!
points
^ points! !
!Form methodsFor: '*Pen-filling' stamp: 'HilaireFernandes 11/8/2017 11:55:59'!
findShapeAroundSeedBlock: seedBlock
"Build a shape that is black in any region marked by seedBlock.
SeedBlock will be supplied a form, in which to blacken various
pixels as 'seeds'. Then the seeds are smeared until
there is no change in the smear when it fills the region, ie,
when smearing hits a black border and thus goes no further."
| smearForm previousSmear all count smearPort |
self depth > 1 ifTrue: [self halt]. "Only meaningful for B/W forms."
all := self boundingBox.
smearForm := Form extent: self extent.
smearPort := BitBlt toForm: smearForm.
seedBlock value: smearForm. "Blacken seeds to be smeared"
smearPort copyForm: self to: 0 @ 0 rule: Form erase. "Clear any in black"
previousSmear := smearForm copy.
count := 1.
[count = 10 and: "check for no change every 10 smears"
[count := 1.
previousSmear copy: all from: 0 @ 0 in: smearForm rule: Form reverse.
previousSmear isAllWhite]]
whileFalse:
[smearPort copyForm: smearForm to: 1 @ 0 rule: Form under.
smearPort copyForm: smearForm to: -1 @ 0 rule: Form under.
"After horiz smear, trim around the region border"
smearPort copyForm: self to: 0 @ 0 rule: Form erase.
smearPort copyForm: smearForm to: 0 @ 1 rule: Form under.
smearPort copyForm: smearForm to: 0 @ -1 rule: Form under.
"After vert smear, trim around the region border"
smearPort copyForm: self to: 0 @ 0 rule: Form erase.
count := count + 1.
count = 9 ifTrue: "Save penultimate smear for comparison"
[previousSmear copy: all from: 0 @ 0 in: smearForm rule: Form over]].
"Now paint the filled region in me with aHalftone"
^ smearForm! !