summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2019-09-29 08:27:31 +0200
committerLars-Dominik Braun <lars@6xq.net>2019-09-29 08:27:31 +0200
commit190937bd2f5660ae8db71456ddbf5de22e9bc608 (patch)
tree67793f2dc5c8cca3bb2b2fcd0abd2503440b2c92
parentbc0bd6a2f4a10516e89d3af1ea38e8203219fdff (diff)
downloadlulua-190937bd2f5660ae8db71456ddbf5de22e9bc608.tar.gz
lulua-190937bd2f5660ae8db71456ddbf5de22e9bc608.tar.bz2
lulua-190937bd2f5660ae8db71456ddbf5de22e9bc608.zip
carpalx: Add model by Salvo et al.
-rw-r--r--doc/Makefile3
-rw-r--r--lulua/carpalx.py103
-rw-r--r--lulua/optimize.py11
-rw-r--r--lulua/plot.py4
-rw-r--r--lulua/stats.py4
-rw-r--r--lulua/test_carpalx.py4
6 files changed, 115 insertions, 14 deletions
diff --git a/doc/Makefile b/doc/Makefile
index 0cd56a4..e280a53 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -5,6 +5,7 @@ WIKIEXTRACTOR:=../3rdparty/wikiextractor/WikiExtractor.py
OPTROUNDS=100000
# pin layers, keep hand-optimized numbers, keep top row free
OPTPINS='0;1;2;0,Bl1;0,Bl2;0,Bl3;0,Bl4;0,Bl5;0,Bl6;0,Bl7;0,Br6;0,Br5;0,Br4;0,Br3;0,Br2;0,Br1;3,Cl4;3,Cl3;3,Cl2;3,Cl1;3,Dl4;3,Dl3;3,Dl2;3,Dl1;3,El5;3,El4;3,El3;3,El2;3,Dl5;3,Cl5;3,El6'
+OPTMODEL=mod01
all: ar-lulua.xmodmap ar-lulua.svg ar-asmo663.svg ar-linux.svg ar-malas.svg ar-phonetic.svg ar-osman.svg letterfreq.json ar-khorshid.svg
@@ -56,5 +57,5 @@ $(STATSDIR)/stats.pickle: $(STATSDIR)/stats-arwiki.pickle \
### optimization ###
optimized.yaml: $(STATSDIR)/stats.pickle
- lulua-optimize -n $(OPTROUNDS) -r -p $(OPTPINS) -l ar-lulua < $< > $@
+ lulua-optimize -n $(OPTROUNDS) -r -p $(OPTPINS) -l ar-lulua -m $(OPTMODEL) < $< > $@
diff --git a/lulua/carpalx.py b/lulua/carpalx.py
index 3e104bb..e17b6e4 100644
--- a/lulua/carpalx.py
+++ b/lulua/carpalx.py
@@ -43,8 +43,9 @@ from .keyboard import Button
ModelParams = namedtuple ('ModelParams', ['kBPS', 'k123S',
'w0HRF', 'pHand', 'pRow', 'pFinger', 'fHRF', 'baselineEffort'])
+models = dict (
# model parameters mod_01 from http://mkweb.bcgsc.ca/carpalx/?model_parameters
-model01 = ModelParams (
+mod01 = ModelParams (
# k_b, k_p, k_s
kBPS = (0.3555, 0.6423, 0.4268),
# k_1, k_2, k_3 plus extension k_S (weight for simultaneous key presses)
@@ -132,7 +133,105 @@ model01 = ModelParams (
'Fr_altgr': 4.0, # XXX: dito
},
- )
+ ),
+
+# from paper “Ergonomic Keyboard Layout Designed for the Filipino Language”
+salvo = ModelParams (
+ # k_b, k_p, k_s
+ # XXX: unchanged from original?
+ kBPS = (0.3555, 0.6423, 0.4268),
+ # k_1, k_2, k_3 plus extension k_S (weight for simultaneous key presses)
+ # XXX: unchanged from original?
+ k123S = (1.0, 0.367, 0.235, 1.0),
+ # w0, wHand, wRow, wFinger
+ # XXX: unchanged from original?
+ w0HRF = (0.0, 1.0, 1.3088, 2.5948),
+ pHand = {LEFT: 0.0, RIGHT: 0.0},
+ # numbers, top, base, bottom, control (XXX not part of original model)
+ # XXX: unchanged from original?
+ pRow = (1.5, 0.5, 0.0, 1.0, 1.5),
+ # symmetric penalties
+ # normalized as suggested by https://normanlayout.info/about.html
+ pFinger = {
+ LEFT: {
+ THUMB: 0.0, # XXX: not part of the original model
+ INDEX: 1-(6.09/6.57),
+ MIDDLE: 1-(5.65/6.57),
+ RING: 1-(4.54/6.57),
+ LITTLE: 1-(3.77/6.57),
+ },
+ RIGHT: {
+ THUMB: 0.0, # XXX: not part of the original model
+ INDEX: 1-(6.57/6.57),
+ MIDDLE: 1-(6.37/6.57),
+ RING: 1-(5.08/6.57),
+ LITTLE: 1-(4.27/6.57),
+ },
+ },
+ # fHand, fRow, fFinger
+ fHRF = (1.0, 0.3, 0.3),
+ # baseline key effort
+ baselineEffort = {
+ # XXX: from model 01, not part of the paper
+ 'Bl1': 5.0,
+ 'Bl2': 5.0,
+ 'Bl3': 4.0,
+ 'Bl4': 4.0,
+ 'Bl5': 4.0,
+ 'Bl6': 3.5,
+ 'Bl7': 4.5,
+ 'Br6': 4.0,
+ 'Br5': 4.0,
+ 'Br4': 4.0,
+ 'Br3': 4.0,
+ 'Br2': 4.0,
+ 'Br1': 4.5,
+
+ 'Cl1': 2.0,
+ 'Cl2': 2.0,
+ 'Cl3': 2.0,
+ 'Cl4': 2.0,
+ 'Cl5': 2.3,
+ 'Cr7': 3.0,
+ 'Cr6': 1.9,
+ 'Cr5': 2.0,
+ 'Cr4': 2.0,
+ 'Cr3': 2.2,
+ 'Cr2': 4.0, # XXX: dito
+ 'Cr1': 6.0,
+
+ 'Dl_caps': 2.0, # XXX: dito
+ 'Dl1': 0.0,
+ 'Dl2': 0.0,
+ 'Dl3': 0.0,
+ 'Dl4': 0.0,
+ 'Dl5': 1.8,
+ 'Dr7': 1.8,
+ 'Dr6': 0.0,
+ 'Dr5': 0.0,
+ 'Dr4': 0.0,
+ 'Dr3': 0.0,
+ 'Dr2': 2.0, # XXX: dito
+ 'Dr1': 4.0, # XXX: dito
+
+ 'El_shift': 4.0, # XXX: dito
+ 'El1': 4.0, # XXX: dito
+ 'El2': 2.0,
+ 'El3': 2.0,
+ 'El4': 2.0,
+ 'El5': 2.0,
+ 'El6': 3.5,
+ 'Er5': 2.0,
+ 'Er4': 2.0,
+ 'Er3': 2.0,
+ 'Er2': 2.0,
+ 'Er1': 2.0,
+ 'Er_shift': 4.0, # XXX: dito
+
+ 'Fr_altgr': 4.0, # XXX: dito
+ },
+ ),
+)
def madd (a, b):
""" Given indexables a and b, computes a[0]*b[0]+a[1]*b[1]+… """
diff --git a/lulua/optimize.py b/lulua/optimize.py
index 879f531..281f0ab 100644
--- a/lulua/optimize.py
+++ b/lulua/optimize.py
@@ -32,8 +32,7 @@ tqdm.get_lock().locks = []
import yaml
from .layout import defaultLayouts, ButtonCombination, Layer, KeyboardLayout, GenericLayout
-from .carpalx import Carpalx
-from .carpalx import model01 as cmodel01
+from .carpalx import Carpalx, models, ModelParams
from .writer import Writer
from .util import first
from .keyboard import defaultKeyboards, LetterButton
@@ -147,8 +146,9 @@ class LayoutOptimizer (Annealer):
triads: List[Tuple[ButtonCombination]],
layout: KeyboardLayout,
pins: FrozenSet[Tuple[int, Optional[Text]]],
- writer: Writer):
- carpalx = Carpalx (cmodel01, writer)
+ writer: Writer,
+ model: ModelParams):
+ carpalx = Carpalx (model, writer)
super ().__init__ (LayoutOptimizerState (carpalx, buttonMap))
self.triads = triads
@@ -256,6 +256,7 @@ def optimize ():
parser.add_argument('-n', '--steps', type=int, default=10000, help='Number of iterations')
parser.add_argument('-r', '--randomize', action='store_true', help='Randomize layout before optimizing')
parser.add_argument('-p', '--pin', type=parsePin, help='Pin these layers/buttons')
+ parser.add_argument('-m', '--model', choices=list (models.keys()), default='mod01', help='Carpalx model')
args = parser.parse_args()
@@ -296,7 +297,7 @@ def optimize ():
pins = [(x, keyboard[y] if y else None) for x, y in args.pin]
- opt = LayoutOptimizer (buttonMap, triads, layout, pins, writer)
+ opt = LayoutOptimizer (buttonMap, triads, layout, pins, writer, model=models[args.model])
if args.randomize:
logging.info ('randomizing initial layout')
for i in range (len (buttonMap)*2):
diff --git a/lulua/plot.py b/lulua/plot.py
index 453c6eb..14b9732 100644
--- a/lulua/plot.py
+++ b/lulua/plot.py
@@ -28,7 +28,7 @@ from .layout import *
from .keyboard import defaultKeyboards
from .util import limit, displayText
from .writer import Writer
-from .carpalx import Carpalx, model01
+from .carpalx import Carpalx, models
def letterfreq (args):
""" Map key combinations to their text, bin it and plot sorted distribution """
@@ -124,7 +124,7 @@ def triadfreq (args):
# letter-based binning, in case multiple buttons are mapped to the same
# letter.
- binned = defaultdict (lambda: dict (weight=0, effort=Carpalx (model01, writer), textTriad=None))
+ binned = defaultdict (lambda: dict (weight=0, effort=Carpalx (models['mod01'], writer), textTriad=None))
weightSum = 0
for triad, weight in stats['triads'].triads.items ():
textTriad = tuple (layout.getText (t) for t in triad)
diff --git a/lulua/stats.py b/lulua/stats.py
index 6689616..4cd20bd 100644
--- a/lulua/stats.py
+++ b/lulua/stats.py
@@ -26,7 +26,7 @@ from collections import defaultdict
from .layout import *
from .keyboard import defaultKeyboards
from .writer import SkipEvent, Writer
-from .carpalx import Carpalx, model01 as cmodel01
+from .carpalx import Carpalx, models
from .plot import letterfreq, triadfreq
def updateDictOp (a, b, op):
@@ -217,7 +217,7 @@ def pretty (args):
for triad, count in sorted (stats['triads'].triads.items (), key=itemgetter (1)):
print (f'{triad} {count:10d}')
- effort = Carpalx (cmodel01, writer)
+ effort = Carpalx (models['mod01'], writer)
effort.addTriads (stats['triads'].triads)
print ('total effort (carpalx)', effort.effort)
diff --git a/lulua/test_carpalx.py b/lulua/test_carpalx.py
index ac72a14..1d07763 100644
--- a/lulua/test_carpalx.py
+++ b/lulua/test_carpalx.py
@@ -20,7 +20,7 @@
import pytest
-from .carpalx import Carpalx, model01, ModelParams
+from .carpalx import Carpalx, models, ModelParams
from .keyboard import defaultKeyboards
from .layout import defaultLayouts, LEFT, RIGHT, INDEX, MIDDLE, RING, LITTLE
from .writer import Writer
@@ -101,7 +101,7 @@ def test_strokePath (t, i, expect):
keyboard = defaultKeyboards['ibmpc105']
layout = defaultLayouts['ar-linux'].specialize (keyboard)
writer = Writer (layout)
- c = Carpalx (model01, writer)
+ c = Carpalx (models['mod01'], writer)
t = tuple (map (keyboard.find, t))
assert c._strokePath (t)[i] == expect