-
Notifications
You must be signed in to change notification settings - Fork 1
/
BezierView.applescript
438 lines (375 loc) · 17.5 KB
/
BezierView.applescript
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
--
-- BezierView.applescript
-- PenPressurizer
--
-- Created by Eric Nitardy on 8/25/10.
-- Copyright 2010. All rights reserved.
-- See license.txt file for details.
global backgroundColor, gridColor, linesColor, minPress, maxPress, pressLevels, hardness, controlPt, penButton, prefFilePath, prefFileName, PenTabletDriverName
--property NSGraphicsContext : class "NSGraphicsContext"
property NSAffineTransform : class "NSAffineTransform"
property NSBezierPath : class "NSBezierPath"
property NSColor : class "NSColor"
script BezierView
property parent : class "NSView"
-- "Stylus Button" popup button
property buttonButton : missing value -- NSPopUpButton
-- "Button Feel", "Min. Pressure", "Max. Pressure" sliders:
property hardnessSlider : missing value -- NSSlider
property minPressSlider : missing value
property maxPressSlider : missing value
on initWithFrame_(frame) -- First handler to run
-- Determine tablet driver's correct application name. Two methods provided.
-- First if process is running, second if not.
tell application "System Events"
set tabletDrivers to every process whose (name contains "TabletDriver" and name is not "TabletDriver")
end tell
if tabletDrivers is {} then
-- tablet driver process is not running, so ...
try
set AppScriptDriver to "TabletDriver"
set tabletPath to path to application AppScriptDriver
tell application "Finder"
set tabletApp to name of folder of folder of folder of tabletPath
end tell
set PenTabletDriverName to name of application tabletApp
tell application PenTabletDriverName to launch
on error -- Both methods for finding tablet driver's app name failed, so ...
tell current application
activate
display alert "No TabletDriver running." message "Perhaps this system is not attached to a Wacom Pen Tablet. Nonetheless, this Application cannot launch." as warning
end tell
current application's NSApp's terminate_(me)
end try
else -- tablet driver process IS running, so ...
set PenTabletDriverName to name of item 1 of tabletDrivers
end if
-- Find Wacom preferences file. If not found, display error.
set prefFileName to "com.wacom.pentablet.prefs"
set prefFilePath to getprefFilePath(prefFileName)
if prefFilePath is "not found" then
tell current application
activate
display alert "No PenTablet preference file found." message "Perhaps this system is not attached to a Wacom Pen Tablet. Nonetheless, this Application cannot launch." as warning
end tell
current application's NSApp's terminate_(me)
end if
continue initWithFrame_(frame)
-- Set Bézier view's colors
set backgroundColor to NSColor's whiteColor
set gridColor to NSColor's blackColor
set linesColor to NSColor's blueColor
return me
end initWithFrame_
on awakeFromNib() -- Second handler to run
preparePenPrefFile() -- Make tmp files to speed sed's pref file changes
-- Using "Styus Button" value, find controlPt in pref file
-- Then using controlPt, determine hardness, minPress, maxPress.
set penButton to (buttonButton's titleOfSelectedItem) as string
getControlPtFromPenPref(penButton)
controlPtToHardness(controlPt)
-- Set sliders to correct hardness, minPress, maxPress.
hardnessSlider's setDoubleValue_(hardness)
minPressSlider's setDoubleValue_(minPress)
maxPressSlider's setDoubleValue_(maxPress)
end awakeFromNib
-- When"Styus Button" changes,
on changeButtonButton_(sender)
set penButton0 to (buttonButton's titleOfSelectedItem) as string
if penButton ≠ penButton0 then
-- Using "Styus Button" value, find controlPt in pref file
-- Then using controlPt, determine hardness, minPress, maxPress.
set penButton to penButton0
preparePenPrefFile()
getControlPtFromPenPref(penButton)
controlPtToHardness(controlPt)
-- Set sliders to correct hardness, minPress, maxPress.
hardnessSlider's setDoubleValue_(hardness)
minPressSlider's setDoubleValue_(minPress)
maxPressSlider's setDoubleValue_(maxPress)
my setNeedsDisplay_(true) -- refresh display
end if
end changeButtonButton_
-- When"Min. Pressure" slider changes,
on changeminPressSlider_(sender)
set minPress0 to sender's doubleValue()
if minPress0 ≠ minPress then
set minPress to minPress0
if minPress + 5 > maxPress then -- keep Max. Pressure >> Min. Pressure
set maxPress to minPress + 5
maxPressSlider's setDoubleValue_(maxPress)
end if
my setNeedsDisplay_(true) -- refresh display
end if
end changeminPressSlider_
-- When"Max. Pressure" slider changes,
on changemaxPressSlider_(sender)
set maxPress0 to sender's doubleValue()
if maxPress0 ≠ maxPress then
set maxPress to maxPress0
if maxPress < 5 then -- keep Max. Pressure >> 0
set maxPress to 5
maxPressSlider's setDoubleValue_(maxPress)
end if
if minPress + 5 > maxPress then -- keep Max. Pressure >> Min. Pressure
set minPress to maxPress - 5
minPressSlider's setDoubleValue_(minPress)
end if
my setNeedsDisplay_(true) -- refresh display
end if
end changemaxPressSlider_
-- When"Button Feel" (hardness) slider changes,
on changehardnessSlider_(sender)
set hardness0 to sender's doubleValue()
if hardness ≠ hardness0 then
set hardness to hardness0
hardnessToControlPt(hardness)
my setNeedsDisplay_(true) -- refresh display
end if
end changehardnessSlider_
---- Info on Wacom's preset control points (0 - 6) ----
(*
Wacom Control Points for Pen's Quadratic Bézier Curve
[Control pt. is 2nd of the 3 points of a Quadratic Bézier Curve]
[Control pt. is normalized to a 0 to 100 range]
[going from soft (0) to firm (6)]
0th controlPt : {x:0, y:100} -- dist from y=x :-70.72
1st controlPt : {x:11.67, y:83.14} -- dist from y=x :-50.54
2nd controlPt : {x:28, y:66.08} -- dist from y=x :-26.93
3rd controlPt : {x:45.74, y:50} -- dist from y=x :-3.01
4th controlPt : {x:62.86, y:32.94} -- dist from y=x : 21.16
5th controlPt : {x:80.18, y:16.86} -- dist from y=x : 44.77
6th controlPt : {x:81.14, y:12.94} -- dist from y=x : 48.22
[These points are linearly connected to create a continuous
range of Bézier curves for 0≤hardness≤7.]
*)
-- When the "Min. Pressure" Default button is pushed,
-- set "Min. Pressure" to Wacom's preset based on hardness.
-- hardness 0 - 6: minPress 4.9, 5.88, 6.86, 7.48, 10, 14.9, 20
-- linearly connected to create a continuous range
on pushDefaultButton_(sender) -- NSButton
if hardness < 1 then
set minPress to 4.9 * (1 - hardness) + 5.88 * (hardness - 0)
else if hardness < 2 then
set minPress to 5.88 * (2 - hardness) + 6.86 * (hardness - 1)
else if hardness < 3 then
set minPress to 6.86 * (3 - hardness) + 7.84 * (hardness - 2)
else if hardness < 4 then
set minPress to 7.84 * (4 - hardness) + 10 * (hardness - 3)
else if hardness < 5 then
set minPress to 10 * (5 - hardness) + 14.9 * (hardness - 4)
else
set minPress to 14.9 * (6 - hardness) + 20 * (hardness - 5)
end if
minPressSlider's setDoubleValue_(minPress)
my setNeedsDisplay_(true) -- refresh display
end pushDefaultButton_
-- Convert hardness into a control point
-- using Wacom's preset control points for hardnesses 0 - 6,
-- linearly connected to create a continuous range.
on hardnessToControlPt(hardIndex)
if hardIndex < 1 then
set controlPtX to 0 * (1 - hardIndex) + 11.67 * (hardIndex - 0)
set controlPtY to 100 * (1 - hardIndex) + 83.14 * (hardIndex - 0)
else if hardIndex < 2 then
set controlPtX to 11.67 * (2 - hardIndex) + 28 * (hardIndex - 1)
set controlPtY to 83.14 * (2 - hardIndex) + 66.08 * (hardIndex - 1)
else if hardIndex < 3 then
set controlPtX to 28 * (3 - hardIndex) + 45.74 * (hardIndex - 2)
set controlPtY to 66.08 * (3 - hardIndex) + 50 * (hardIndex - 2)
else if hardIndex < 4 then
set controlPtX to 45.74 * (4 - hardIndex) + 62.86 * (hardIndex - 3)
set controlPtY to 50 * (4 - hardIndex) + 32.94 * (hardIndex - 3)
else if hardIndex < 5 then
set controlPtX to 62.86 * (5 - hardIndex) + 80.18 * (hardIndex - 4)
set controlPtY to 32.94 * (5 - hardIndex) + 16.86 * (hardIndex - 4)
else
set controlPtX to 80.18 * (6 - hardIndex) + 81.14 * (hardIndex - 5)
set controlPtY to 16.86 * (6 - hardIndex) + 12.94 * (hardIndex - 5)
end if
set controlPt to {x:controlPtX, y:controlPtY}
end hardnessToControlPt
-- Convert a control point into a hardness
-- using Wacom's preset control points for hardnesses 0 - 6,
-- linearly connected to create a continuous range.
on controlPtToHardness(pt)
set dist to ((pt's x) - (pt's y)) * (2 ^ (-0.5))
if dist < -50.54 then
set hardness to 1 * (dist - -70.72) / (70.72 - 50.54)
else if dist < -26.93 then
set hardness to 1 * (-26.93 - dist) / (50.54 - 26.93) + 2 * (dist - -50.54) / (50.54 - 26.93)
else if dist < -3.01 then
set hardness to 2 * (-3.01 - dist) / (26.93 - 3.01) + 3 * (dist - -26.93) / (26.93 - 3.01)
else if dist < 21.16 then
set hardness to 3 * (21.16 - dist) / (21.16 + 3.01) + 4 * (dist - -3.01) / (21.16 + 3.01)
else if dist < 44.77 then
set hardness to 4 * (44.77 - dist) / (44.77 - 21.16) + 5 * (dist - 21.16) / (44.77 - 21.16)
else
set hardness to 5 * (48.22 - dist) / (48.22 - 44.77) + 6 * (dist - 44.77) / (48.22 - 44.77)
end if
if hardness > 7 then set hardness to 7
if hardness < 0 then set hardness to 0
end controlPtToHardness
-- ReDraw Bézier view
on drawRect_(dirtyRect)
set |bounds| to my |bounds|()
--set |bounds| to dirtyRect
set emptySpace to 10
--set currentContext to NSGraphicsContext's currentContext
--Make translation a NSAffineTransform type object
set translation to NSAffineTransform's transform
-- rControlPt: ControlPt adjusted for range between minPress and maxPress
set theRange to (maxPress - minPress) / 100
set rControlPt to ¬
{x:(controlPt's x) * theRange + minPress, y:(controlPt's y)}
-- vControlPt: effective on screen ControlPt — view 200 px (+) wide
set vControlPt to {x:(rControlPt's x) * 2, y:(rControlPt's y) * 2}
-- Draw background
set thePath to NSBezierPath's bezierPathWithRect_(|bounds|)
thePath's setLineWidth_(0.5)
thePath's stroke()
backgroundColor's |set|()
thePath's fill()
thePath's release()
-- Draw x(Pen Pressure) and y(Tablet Response) axes
gridColor's |set|()
thePath's setLineWidth_(0.75)
thePath's moveToPoint_({x:2, y:0})
thePath's lineToPoint_({x:2, y:220})
thePath's moveToPoint_({x:0, y:2})
thePath's lineToPoint_({x:250, y:2})
thePath's stroke()
thePath's release()
-- Draw Max pressure and Max response grid lines
thePath's setLineWidth_(0.5)
thePath's moveToPoint_({x:202, y:0})
thePath's lineToPoint_({x:202, y:220})
thePath's moveToPoint_({x:0, y:202})
thePath's lineToPoint_({x:250, y:202})
thePath's stroke()
thePath's release()
-- Draw Pressure-Response curve
linesColor's |set|()
set thePath to NSBezierPath's bezierPath
thePath's moveToPoint_({x:0, y:0})
thePath's lineToPoint_({x:(minPress * 2) as integer, y:0}) -- to minpress flat
thePath's ¬
curveToPoint_controlPoint1_controlPoint2_({x:(maxPress * 2) as integer, y:200}, vControlPt, vControlPt) -- curve from minpress to maxpress
thePath's lineToPoint_({x:250, y:200}) -- from maxpress flat
thePath's setLineCapStyle_(1) -- I'm puzzled by the effect of changing this
thePath's setLineWidth_(3)
-- True origin for pressure curve is (2, 2) — see axes above
translation's translateXBy_yBy_(2, 2)
translation's concat()
thePath's stroke()
thePath's release()
-- pControlPt: effective pref file ControlPt
-- adjusted for actual tablet pressure sensitivity (pressLevels)
set pControlPt to ¬
{x:((rControlPt's x) / 100 * pressLevels) as integer, y:((rControlPt's y) / 100 * pressLevels) as integer}
-- pMinPress, pMinPress: effective pref file values
-- adjusted for actual tablet pressure sensitivity (pressLevels)
set pMinPress to (minPress / 100 * pressLevels) as integer
set pMaxPress to (maxPress / 100 * pressLevels) as integer
-- Adjust PressureCurveControlPoint, LowerPressureThreshold and
-- UpperPressureThreshold in pen preferences file based on new values of
-- pMinPress, pMaxPress, pControlPt
setcontrolPtInPrefFile(pMinPress, pMaxPress, pControlPt)
end drawRect_
-- Find Wacom preferences file. If not found, return "not found"
-- otherwise return path to file.
on getprefFilePath(fileName)
set pLocal to path to preferences from local domain
set pUser to path to preferences from user domain
set pUserX to POSIX path of pUser
set pLocalX to POSIX path of pLocal
tell application "Finder"
if exists file fileName in folder pUser then
set pFilePath to pUserX & fileName
else if exists file fileName in folder pLocal then
set pFilePath to pLocalX & fileName
else
set pFilePath to "not found"
end if
end tell
return pFilePath
end getprefFilePath
-- Create new temp file of pen preferences file ("new2")
-- with line endings translated from Mac to Unix
-- Part of prep for speeding up sed
on preparePenPrefFile()
set tempFile to (POSIX path of (path to temporary items)) & "new2"
do shell script ¬
"/bin/cat " & quoted form of prefFilePath & " | /usr/bin/tr \"\\r\" \"\\n\" >" & tempFile
end preparePenPrefFile
-- Use sed on "new2" temp file to find controlPtData and pressLevelData
-- for chosen stylus button (tip or eraser)
-- Then use sed to put markers in new temp file ("new3")
-- where data needs to be placed. This file has Mac line endings
-- and is used with sed to quickly insert new data into pen pref file.
on getControlPtFromPenPref(buttonName)
set tempFile to (POSIX path of (path to temporary items)) & "new2"
set tempFile1 to (POSIX path of (path to temporary items)) & "new3"
-- Find in file 1st "PressureCurveControlPoint" after button name
-- and extract controlPtData
set controlPtData to do shell script ¬
"/usr/bin/sed -n '/ButtonName " & buttonName & "/,/PressureCurveControlPoint/ s_\\(PressureCurveControlPoint [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]*\\)_\\1_p' " & tempFile
-- Find in file 1st "PressureResolution" after button name
-- and extract pressLevelData
set pressLevelData to do shell script ¬
"/usr/bin/sed -n '/ButtonName " & buttonName & "/,/PressureResolution/ s_\\(PressureResolution [0-9][0-9]*\\)_\\1_p' " & tempFile
-- Calculate variables with data
-- mostly 0 - 100 normalization based on pressLevels
set pressLevels to (word 2 of pressLevelData) as integer
set minPress to ((word 2 of controlPtData) as integer) / pressLevels * 100
set maxPress to ((word 6 of controlPtData) as integer) / pressLevels * 100
set theRange to (maxPress - minPress) / 100
set controlPtX to ((word 4 of controlPtData) as integer) / pressLevels * 100
set controlPtY to ((word 5 of controlPtData) as integer) / pressLevels * 100
set controlPt to {x:(controlPtX - minPress) / theRange, y:controlPtY}
-- Insert markers in new Mac line ending file ("new3")
-- where sed will insert data for changed pref file.
-- Three sed substitutions.
do shell script ¬
"/usr/bin/sed -e '/ButtonName " & buttonName & "/,/PressureCurveControlPoint/ s_\\(PressureCurveControlPoint\\) [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]* [0-9][0-9]*_\\1 --controlPt goes here--_' -e '/ButtonName " & buttonName & "/,/UpperPressureThreshold/ s_\\(UpperPressureThreshold\\) [0-9][0-9]*_\\1 --minPressHi goes here--_' -e '/ButtonName " & buttonName & "/,/LowerPressureThreshold/ s_\\(LowerPressureThreshold\\) [0-9][0-9]*_\\1 --minPressLo goes here--_' " & tempFile & " | /usr/bin/tr \"\\n\" \"\\r\" > " & tempFile1
end getControlPtFromPenPref
-- Adjust PressureCurveControlPoint, LowerPressureThreshold and
-- UpperPressureThreshold in pen preferences file based on new values of
-- pMinPress, pMaxPress, pControlPt
on setcontrolPtInPrefFile(pMinPress, pMaxPress, pControlPt)
set tempFile1 to (POSIX path of (path to temporary items)) & "new3"
set tempFile2 to prefFilePath
---- Convert variable values to strings --
set sPressLevels to pressLevels as string
if pMinPress ≤ 3 then set pMinPress to 4 -- minimum value
set sMinPress to pMinPress as string
-- keep LowerPressureThreshold & UpperPressureThreshold three apart
set sMinPressHi to (pMinPress + 1) as string
set sMinPressLo to (pMinPress - 2) as string
set sMaxPress to pMaxPress as string
set sPtX to pControlPt's x as string
set sPtY to pControlPt's y as string
-- Insert data at marker locations in Mac line ending file ("new3")
-- and result is streamed into Wacom pen preferences file.
-- Three sed substitutions.
do shell script ¬
"/usr/bin/sed -e 's_--controlPt goes here--_" & sMinPress & " 0 " & sPtX & " " & sPtY & " " & sMaxPress & " " & sPressLevels & "_' -e 's_--minPressHi goes here--_" & sMinPressHi & "_' -e 's_--minPressLo goes here--_" & sMinPressLo & "_' " & tempFile1 & " > " & tempFile2
resetTabletDriver() -- force tablet driver to use new preferences data
end setcontrolPtInPrefFile
-- This handler is a fairly unsophisticated approach to getting the driver to
-- recognize settings changes, but it's the best I've got so far.
-- When "Button Feel" (hardness) slider, is re-adjusted repeatedly
-- and in quick sucession, the driver can freeze or behave oddly.
-- Force quitting the driver or hitting the Modbook reset button fixes this.
on resetTabletDriver()
try
do shell script "killall " & quoted form of PenTabletDriverName
on error
try
do shell script "killall -9 " & quoted form of PenTabletDriverName
end try
end try
delay 0.2
tell application PenTabletDriverName to launch
end resetTabletDriver
end script