Skip to content

Commit

Permalink
Merge pull request #17 from semilin/v1.2.0
Browse files Browse the repository at this point in the history
V1.2.0
  • Loading branch information
semilin authored Feb 11, 2024
2 parents 3910809 + 0d453fe commit 16e6c97
Show file tree
Hide file tree
Showing 19 changed files with 675 additions and 356 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# genkey
Note: genkey 1.2 is the last major version of genkey. It mostly consists of a cleaned up codebase with less bugs, so that its code can maybe be helpful for someone. I will no longer make any significant changes to the project, but pull requests are welcome. See the Alternatives section.

Genkey is a powerful tool for analyzing and creating keyboard layouts that was used to create [Semimak](https://semilin.github.io/blog/2021/semimak.html). It features detailed analysis output, generation, customizable weighting, individual layout improvement, heatmap output, interactive analysis, and much more!

For installation instructions and documentation, see [the website](https://semilin.github.io/genkey).

## Alternatives
- For what is essentially a better version of genkey, I recommend Oxey's [Oxeylyzer](https://github.com/o-x-e-y/oxeylyzer).
- For something very different, I'm currently working on a graphical analyzer called [Keymui](https://github.com/semilin/). It's much more powerful and flexible than genkey.

## License
Copyright © 2021 semi
Copyright © 2024 semi

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
57 changes: 47 additions & 10 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
package main

import (
"encoding/json"
"fmt"
"github.com/hjson/hjson-go"
"io/ioutil"
"os"
"path/filepath"

"github.com/BurntSushi/toml"
)

func ReadWeights() {
b, err := ioutil.ReadFile("weights.hjson")
func fileExists(path string) bool {
if _, err := os.Stat(path); err == nil {
return true
}
return false
}

func findConfig() string {
if fileExists("./config.toml") {
return "config.toml"
}
config_dir := os.Getenv("XDG_CONFIG_DIR")
path := filepath.Join(config_dir, "genkey", "config.toml")
if fileExists(path) {
return path
}
home, err := os.UserHomeDir()
if err != nil {
fmt.Printf("There was an issue reading the weights file.\nPlease make sure there is a 'weights.json' in this directory.")
panic(err)
}
path = filepath.Join(home, ".config", "genkey", "config.toml")
if fileExists(path) {
return path
}

var dat map[string]interface{}
println("Couldn't find config.toml in any of local directory, $XDG_CONFIG_DIR/genkey/config.toml, or ~/.config/genkey/config.toml.")
os.Exit(1)
return ""
}

err = hjson.Unmarshal(b, &dat)
func ReadWeights() {
path := findConfig()
b, err := os.ReadFile(path)
if err != nil {
fmt.Printf("There was an issue reading the config file.")
panic(err)
}

_, err = toml.Decode(string(b), &Config)
if err != nil {
panic(err)
}

j, _ := json.Marshal(dat)
json.Unmarshal(j, &Weight)
if !fileExists(filepath.Join(Config.Paths.Corpora, Config.Corpus) + ".json") {
fmt.Printf("Invalid config: Corpus [%s] does not exist.\n", Config.Corpus)
os.Exit(1)
}

if Config.Generation.Selection > Config.Generation.InitialPopulation {
fmt.Println("Invalid config: Generation.Selection cannot be greater than Generation.InitialPopulation.")
os.Exit(1)
}
}
95 changes: 95 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
Corpus = "shai-iweb"

[Output]
# Enables heatmap output after layout generation.
Generation.Heatmap = true

# Char to fill the space in the rank display.
Rank.Spacer = ' '

# The number of most frequent ngrams to display in `genkey analysis`.
Analysis.TopNgrams = 8

# The number of most frequent ngrams to display by default in commands
# like `genkey sfbs`.
Misc.TopNgrams = 20

[Paths]
# The paths that genkey should operate using. Useful if running genkey
# as a standalone executable rather than using a dedicated directory.

Layouts = "./layouts"
Corpora = "./corpora"
Heatmap = "./heatmap.png"

[Weights]
Dist.Lateral = 1.4 # Lateral movement multiplier

# Set to true to measure distances for row-stagger keyboard.
# Otherwise measures based on ortholinear distances.
# Can be overriden using the -stagger flag.
Stagger = false

[Weights.Fspeed]
SFB = 1.0 # Weight of sfbs
DSFB = 0.5 # Weight of dsfbs
KeyTravel = 0.01 # How much baseline distance there is per keypress

# Keys per second, or how dexterous each finger is
KPS = [
1.5, # lp
3.6, # lr
4.8, # lm
5.5, # li
5.5, # ri
4.8, # rm
3.6, # rr
1.5, # rp
]

[Weights.Score]
Fspeed = 3 # Weight of fspeed
IndexBalance = 0.3 # Weight of difference in usage between index fingers
Lsb = 1 # Weight of lsb frequency

[Weights.Score.Trigrams]
# No trigrams will be calculated if enabled = false
Enabled = false

# Number of most frequent trigrams to analyze.
# Set to 0 to analyze all trigrams (will slow down analysis a lot).
Precision = 100

LeftInwardRoll = 0
LeftOutwardRoll = 0
RightInwardRoll = 0
RightOutwardRoll = 0

Alternate = 0
Redirect = 0
Onehand = 0

[Generation]
# The characters that generated layouts will consist of.
GeneratedLayoutChars = "abcdefghijklmnopqrstuvwxyz,./'"
# The number of random layouts to be optimized at start of generation.
InitialPopulation = 1000
# The number of best layouts out of the initial population to run full
# improvement on. Must be less than InitialPopulation.
Selection = 100

[CorpusProcessing]
# Describes how to process new corpora with `genkey load`. Doesn't
# apply to already processed corpora.

# The chars which are allowed to be included in ngrams.
ValidChars = "abcdefghijklmnopqrstuvwxyz,./?;:-_'\""
# Shift pairs which are to be treated as the same character.
CharSubstitutions = [["?", "/"],
[":", ";"],
["_", "-"],
["\"", "'"]]
# The largest size of skipgram to be used for DSFB calculation.
MaxSkipgramSize = 10
# Set to false to include skipgrams which skip over chars not in ValidChars.
SkipgramsMustSpanValidChars = true
1 change: 1 addition & 0 deletions corpora/shai-iweb.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions corpora/tr.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion data.json

This file was deleted.

1 change: 0 additions & 1 deletion data/books.json

This file was deleted.

1 change: 0 additions & 1 deletion data/shai.json

This file was deleted.

1 change: 0 additions & 1 deletion data/words.json

This file was deleted.

31 changes: 16 additions & 15 deletions generate.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2021 Colin Hughes
Copyright (C) 2024 semi
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
Expand Down Expand Up @@ -29,7 +29,7 @@ import (

func Score(l Layout) float64 {
var score float64
s := &Weight.Score
s := &Config.Weights.Score
if s.FSpeed != 0 {
var speeds []float64
if !DynamicFlag {
Expand All @@ -46,15 +46,15 @@ func Score(l Layout) float64 {
if s.LSB != 0 {
score += s.LSB * 100 * float64(LSBs(l)) / l.Total
}
if s.TrigramPrecision != -1 {
tri := FastTrigrams(l, s.TrigramPrecision)
score += s.LeftInwardRoll * (100 - (100 * float64(tri.LeftInwardRolls) / float64(tri.Total)))
score += s.RightInwardRoll * (100 - (100 * float64(tri.RightInwardRolls) / float64(tri.Total)))
score += s.LeftOutwardRoll * (100 - (100 * float64(tri.LeftOutwardRolls) / float64(tri.Total)))
score += s.RightOutwardRoll * (100 - (100 * float64(tri.RightOutwardRolls) / float64(tri.Total)))
score += s.Alternate * (100 - (100 * float64(tri.Alternates) / float64(tri.Total)))
score += s.Onehand * (100 - (100 * float64(tri.Onehands) / float64(tri.Total)))
score += s.Redirect * (100 * float64(tri.Redirects) / float64(tri.Total))
if s.Trigrams.Enabled {
tri := FastTrigrams(&l, s.Trigrams.Precision)
score += s.Trigrams.LeftInwardRoll * (100 - (100 * float64(tri.LeftInwardRolls) / float64(tri.Total)))
score += s.Trigrams.RightInwardRoll * (100 - (100 * float64(tri.RightInwardRolls) / float64(tri.Total)))
score += s.Trigrams.LeftOutwardRoll * (100 - (100 * float64(tri.LeftOutwardRolls) / float64(tri.Total)))
score += s.Trigrams.RightOutwardRoll * (100 - (100 * float64(tri.RightOutwardRolls) / float64(tri.Total)))
score += s.Trigrams.Alternate * (100 - (100 * float64(tri.Alternates) / float64(tri.Total)))
score += s.Trigrams.Onehand * (100 - (100 * float64(tri.Onehands) / float64(tri.Total)))
score += s.Trigrams.Redirect * (100 * float64(tri.Redirects) / float64(tri.Total))
}

if s.IndexBalance != 0 {
Expand All @@ -68,7 +68,7 @@ func Score(l Layout) float64 {
}

func randomLayout() Layout {
chars := "abcdefghijklmnopqrstuvwxyz,./'"
chars := Config.Generation.GeneratedLayoutChars
var k [][]string
k = make([][]string, 3)
var l Layout
Expand Down Expand Up @@ -117,7 +117,6 @@ func sortLayouts(layouts []layoutScore) {
}

func Populate(n int) Layout {
rand.Seed(time.Now().Unix())
layouts := []layoutScore{}
for i := 0; i < n; i++ {
if !ImproveFlag {
Expand Down Expand Up @@ -151,7 +150,7 @@ func Populate(n int) Layout {
PrintLayout(layouts[2].l.Keys)
fmt.Println(Score(layouts[2].l))

layouts = layouts[0:100]
layouts = layouts[0:Config.Generation.Selection]

for i := range layouts {
layouts[i].score = 0
Expand All @@ -178,7 +177,9 @@ func Populate(n int) Layout {
}

PrintAnalysis(best.l)
Heatmap(best.l)
if Config.Output.Generation.Heatmap {
Heatmap(best.l)
}

//improved := ImproveRedirects(layouts[0].keys)
//PrintAnalysis("Generated (improved redirects)", improved)
Expand Down
82 changes: 60 additions & 22 deletions globals.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2021 Colin Hughes
Copyright (C) 2024 semi
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
Expand All @@ -25,33 +25,71 @@ var FingerNames = [8]string{"LP", "LR", "LM", "LI", "RI", "RM", "RR", "RP"}
var Layouts map[string]Layout
var GeneratedFingermap map[Finger][]Pos
var GeneratedFingermatrix map[Pos]Finger
var LongestLayoutName int

var SwapPossibilities []Pos

var Analyzed int

var Weight struct {
FSpeed struct {
SFB float64
DSFB float64
KeyTravel float64
KPS [8]float64
var Config struct {
Corpus string
Output struct {
Generation struct {
Heatmap bool
}
Rank struct {
Spacer string
}
Analysis struct {
TopNgrams int
}
Misc struct {
TopNgrams int
}
}
Dist struct {
Lateral float64
Paths struct {
Layouts string
Corpora string
Heatmap string
}
Score struct {
FSpeed float64
IndexBalance float64
LSB float64

TrigramPrecision int
LeftInwardRoll float64
LeftOutwardRoll float64
RightInwardRoll float64
RightOutwardRoll float64
Alternate float64
Redirect float64
Onehand float64
Weights struct {
Stagger bool
FSpeed struct {
SFB float64
DSFB float64
KeyTravel float64
KPS [8]float64
}
Dist struct {
Lateral float64
}
Score struct {
FSpeed float64
IndexBalance float64
LSB float64

Trigrams struct {
Enabled bool
Precision int
LeftInwardRoll float64
LeftOutwardRoll float64
RightInwardRoll float64
RightOutwardRoll float64
Alternate float64
Redirect float64
Onehand float64
}
}
}
Generation struct {
GeneratedLayoutChars string
InitialPopulation int
Selection int
}
CorpusProcessing struct {
ValidChars string
CharSubstitutions [][2]string
MaxSkipgramSize int8
SkipgramsMustSpanValidChars bool
}
}
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
module github.com/semilin/genkey

go 1.16
go 1.21

require (
github.com/buger/goterm v1.0.1
github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807
github.com/BurntSushi/toml v1.3.2
github.com/buger/goterm v1.0.4
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/hjson/hjson-go v3.1.0+incompatible
github.com/wayneashleyberry/truecolor v1.0.1
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/image v0.15.0 // indirect
golang.org/x/sys v0.17.0 // indirect
)
Loading

0 comments on commit 16e6c97

Please sign in to comment.