Skip to content

Instantly share code, notes, and snippets.

@basgys
Last active December 19, 2019 23:43
Show Gist options
  • Save basgys/68c06269e6961b76be6ad585606c09e1 to your computer and use it in GitHub Desktop.
Save basgys/68c06269e6961b76be6ad585606c09e1 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math"
"sort"
"strings"
"github.com/pbnjay/clustering"
)
type Palette struct {
Name string
Colours map[string]string
}
type reference struct {
P *Palette
Count int
}
func main() {
palettes := []*Palette{
{
Name: "colours",
Colours: map[string]string{
"tomato": "#FF1212",
"apple": "#11DD22",
},
},
{
Name: "brand",
Colours: map[string]string{
"brand": "#001122",
"brand-light": "#112233",
},
},
{
Name: "grey",
Colours: map[string]string{
"light": "#DDDDDD",
"dark": "#121212",
},
},
{
Name: "aggregated",
Colours: map[string]string{
"ligher": "grey--light",
"light": "brand--brand",
"dark": "grey--dark",
"darker": "interactions--button-primary",
"apple": "colours--apple",
},
},
{
Name: "interactions",
Colours: map[string]string{
"button-primary": "brand--brand",
"button-danger": "colours--tomato",
"button-positive": "colours--apple",
"button-other": "colours--apple",
},
},
{
Name: "notifications",
Colours: map[string]string{
"danger": "colours--tomato",
"positive": "colours--apple",
"other": "colours--apple",
},
},
}
// Build colours index (colour -> palette)
colours := map[string]string{}
for _, p := range palettes {
for ck := range p.Colours {
name := strings.Join([]string{p.Name, ck}, "--")
colours[name] = p.Name
}
}
// Build distance matrix & ref counter
mref := map[string]*reference{}
dmap := clustering.DistanceMap{}
for i := range palettes {
cmap := map[clustering.ClusterItem]float64{}
a := palettes[i].Name
dmap[a] = cmap
mref[a] = &reference{P: palettes[i]}
for j := range palettes {
b := palettes[j].Name
cmap[b] = math.MaxInt8 // Add distance between the two vertices
}
}
// Populate matrix
for _, p := range palettes {
for _, cv := range p.Colours {
other, ok := colours[cv]
if !ok {
continue // Not a pointer to another colour
}
// Inc palettes ref counter
mref[p.Name].Count++
mref[other].Count++
// Reduce distance as we add a new reference
dmap[p.Name][other]--
}
}
// Cluster palettes
clusters := clustering.NewDistanceMapClusterSet(dmap)
chk := clustering.MaxClusters(len(palettes) / 2)
clustering.Cluster(clusters, chk, clustering.CompleteLinkage())
// Enumerate clusters and print palettes
debug("\nClusters")
clusters.EachCluster(-1, func(cluster int) {
clusters.EachItem(cluster, func(x clustering.ClusterItem) {
debug(cluster, x)
})
})
// Output(ish):
//
// 0 colours
// 0 brand
// 0 notifications
// 1 aggregated
// 1 interactions
// 2 grey
// Get ref counter and sort it
refs := make([]*reference, 0, len(palettes))
for _, ref := range mref {
refs = append(refs, ref)
}
sort.Slice(refs, func(i, j int) bool { return refs[i].Count > refs[j].Count })
debug("\nMost referenced palettes")
for _, ref := range refs {
debug(ref.P.Name, ref.Count)
}
// Output:
//
// colours 7
// aggregated 5
// interactions 5
// notifications 3
// brand 2
// grey 2
}
func debug(v ...interface{}) {
fmt.Println(v...)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment