Skip to content
This repository was archived by the owner on Jul 19, 2022. It is now read-only.

Commit abebe9d

Browse files
committed
Add Hashvatars
Add Hashvatars to display a unique avatar from a Unison Hash for Projects using the Unison hexgrid and the design system colors. Each color is picked by harmonizing the first picked color (for the background) via the full gamut colors and then approximating those colors to colors confined in the design system by using rgb difference.
1 parent 76bb32c commit abebe9d

File tree

15 files changed

+905
-20
lines changed

15 files changed

+905
-20
lines changed

elm.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dependencies": {
88
"direct": {
99
"NoRedInk/elm-simple-fuzzy": "1.0.3",
10+
"avh4/elm-color": "1.0.0",
1011
"elm/browser": "1.0.2",
1112
"elm/core": "1.0.5",
1213
"elm/html": "1.0.0",
@@ -24,6 +25,7 @@
2425
"j-maas/elm-ordered-containers": "1.0.0",
2526
"krisajenkins/remotedata": "6.0.1",
2627
"mgold/elm-nonempty-list": "4.1.0",
28+
"noahzgordon/elm-color-extra": "1.0.2",
2729
"stoeffel/set-extra": "1.2.3",
2830
"wernerdegroot/listzipper": "4.0.0"
2931
},
@@ -33,12 +35,14 @@
3335
"elm/random": "1.0.0",
3436
"elm/time": "1.0.0",
3537
"elm/virtual-dom": "1.0.2",
38+
"fredcy/elm-parseint": "2.0.1",
3639
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
3740
}
3841
},
3942
"test-dependencies": {
4043
"direct": {
41-
"elm-explorations/test": "1.2.2"
44+
"elm-explorations/test": "1.2.2",
45+
"TSFoster/elm-tuple-extra": "2.0.0"
4246
},
4347
"indirect": {}
4448
}

src/Color/Harmony.elm

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
module Color.Harmony exposing (..)
2+
3+
import Color exposing (Color)
4+
import List.Extra as ListE
5+
6+
7+
harmonizesWith : Color -> List Color
8+
harmonizesWith color =
9+
let
10+
complementary_ =
11+
complementary color
12+
13+
( analogousA, analogousB ) =
14+
analogous color
15+
16+
( triadicA, triadicB ) =
17+
triadic color
18+
19+
( splitA, splitB ) =
20+
splitComplementary color
21+
22+
( squareA, squareB, squareC ) =
23+
square color
24+
25+
( tetridicA, tetridicB, tetridicC ) =
26+
tetridic color
27+
28+
harmonizesWith_ =
29+
[ complementary_
30+
, analogousA
31+
, analogousB
32+
, triadicA
33+
, triadicB
34+
, splitA
35+
, splitB
36+
, squareA
37+
, squareB
38+
, squareC
39+
, tetridicA
40+
, tetridicB
41+
, tetridicC
42+
]
43+
in
44+
ListE.uniqueBy Color.toCssString harmonizesWith_
45+
46+
47+
{-| RGB Difference <https://en.wikipedia.org/wiki/Color_difference> - alpha is disregarded
48+
-}
49+
difference : Color -> Color -> Float
50+
difference a b =
51+
let
52+
a_ =
53+
toRgb255 a
54+
55+
b_ =
56+
toRgb255 b
57+
58+
sum =
59+
toFloat (((a_.red - b_.red) ^ 2) + ((a_.blue - b_.blue) ^ 2) + ((a_.green - b_.green) ^ 2))
60+
in
61+
sqrt sum
62+
63+
64+
toRgb255 : Color -> { red : Int, green : Int, blue : Int }
65+
toRgb255 c =
66+
let
67+
rgba =
68+
Color.toRgba c
69+
in
70+
{ red = floor (rgba.red * 255)
71+
, green = floor (rgba.red * 255)
72+
, blue = floor (rgba.blue * 255)
73+
}
74+
75+
76+
{-| Opposites on the color wheel
77+
-}
78+
complementary : Color -> Color
79+
complementary color =
80+
hueAdd 180 color
81+
82+
83+
{-| Adjacent colors on the color wheel
84+
-}
85+
analogous : Color -> ( Color, Color )
86+
analogous color =
87+
( hueAdd 30 color
88+
, hueSubtract 30 color
89+
)
90+
91+
92+
triadic : Color -> ( Color, Color )
93+
triadic color =
94+
( hueAdd 120 color
95+
, hueAdd 240 color
96+
)
97+
98+
99+
splitComplementary : Color -> ( Color, Color )
100+
splitComplementary color =
101+
( hueAdd 150 color
102+
, hueAdd 210 color
103+
)
104+
105+
106+
square : Color -> ( Color, Color, Color )
107+
square color =
108+
( hueAdd 90 color
109+
, hueAdd 180 color
110+
, hueAdd 270 color
111+
)
112+
113+
114+
tetridic : Color -> ( Color, Color, Color )
115+
tetridic color =
116+
( hueAdd 60 color
117+
, hueAdd 180 color
118+
, hueAdd 240 color
119+
)
120+
121+
122+
123+
-- Internal Helpers
124+
125+
126+
hueAdd : Int -> Color -> Color
127+
hueAdd num color =
128+
let
129+
hsla =
130+
Color.toHsla color
131+
132+
hue =
133+
hsla.hue
134+
|> toAngle
135+
|> (+) num
136+
|> wrap360
137+
|> toPt
138+
in
139+
Color.fromHsla { hsla | hue = hue }
140+
141+
142+
hueSubtract : Int -> Color -> Color
143+
hueSubtract num color =
144+
let
145+
hsla =
146+
Color.toHsla color
147+
148+
hue =
149+
hsla.hue
150+
|> toAngle
151+
|> (-) num
152+
|> wrap360
153+
|> toPt
154+
in
155+
Color.fromHsla { hsla | hue = hue }
156+
157+
158+
toAngle : Float -> Int
159+
toAngle pt =
160+
let
161+
a =
162+
floor (pt * 360)
163+
in
164+
if a > 360 then
165+
360 - (360 - a)
166+
167+
else
168+
a
169+
170+
171+
toPt : Int -> Float
172+
toPt ang =
173+
toFloat ang / 360
174+
175+
176+
wrap360 : Int -> Int
177+
wrap360 h =
178+
let
179+
x =
180+
modBy 360 h
181+
in
182+
if x < 0 then
183+
x + 360
184+
185+
else
186+
x

src/Hashvatar.elm

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
module Hashvatar exposing (..)
2+
3+
import Hash exposing (Hash)
4+
import Hashvatar.HexGrid as HexGrid
5+
import Html exposing (Html, div)
6+
import Html.Attributes exposing (class)
7+
import List.Extra as ListE
8+
import String.Extra exposing (break)
9+
import UI.Color as Color exposing (Color)
10+
11+
12+
view : Hash -> Html msg
13+
view hash =
14+
let
15+
raw =
16+
hash |> Hash.toString |> String.replace "#" ""
17+
18+
numSlots =
19+
5
20+
21+
partLength =
22+
String.length raw // numSlots
23+
24+
parts =
25+
break partLength raw
26+
27+
toCharCodeSum str =
28+
str
29+
|> String.toList
30+
|> List.foldl (\c acc -> acc + Char.toCode c) 0
31+
32+
grid =
33+
parts
34+
|> List.map toCharCodeSum
35+
|> toGrid
36+
|> Maybe.withDefault HexGrid.empty
37+
in
38+
div [ class "hashvatar" ]
39+
[ HexGrid.view grid
40+
]
41+
42+
43+
44+
-- Helpers
45+
46+
47+
getIn : Int -> List Color -> Maybe Color
48+
getIn unmoddedIdx colors_ =
49+
ListE.getAt (modBy (List.length colors_) unmoddedIdx) colors_
50+
51+
52+
toGrid : List Int -> Maybe HexGrid.HexGrid
53+
toGrid slots =
54+
let
55+
selectBg grid_ =
56+
let
57+
background =
58+
getIn grid_.background Color.darkNonGrays
59+
in
60+
Maybe.map
61+
(\bg ->
62+
{ background = bg
63+
, tendrils = grid_.tendrils
64+
, cell1 = grid_.cell1
65+
, cell2 = grid_.cell2
66+
, cell3 = grid_.cell3
67+
}
68+
)
69+
background
70+
71+
selectTendrils grid_ =
72+
let
73+
tendrils =
74+
getIn grid_.tendrils (Color.harmonizesWith grid_.background)
75+
in
76+
Maybe.map
77+
(\tr ->
78+
{ background = grid_.background
79+
, tendrils = tr
80+
, cell1 = grid_.cell1
81+
, cell2 = grid_.cell2
82+
, cell3 = grid_.cell3
83+
}
84+
)
85+
tendrils
86+
87+
selectCells grid_ =
88+
Maybe.map3
89+
(\cell1 cell2 cell3 ->
90+
{ background = grid_.background
91+
, tendrils = grid_.tendrils
92+
, cell1 = cell1
93+
, cell2 = cell2
94+
, cell3 = cell3
95+
}
96+
)
97+
(getIn grid_.cell1 (Color.harmonizesWith grid_.background))
98+
(getIn grid_.cell2 (Color.harmonizesWith grid_.background))
99+
(getIn grid_.cell3 (Color.harmonizesWith grid_.background))
100+
in
101+
Maybe.map5
102+
(\background tendrils cell1 cell2 cell3 ->
103+
{ background = background
104+
, tendrils = tendrils
105+
, cell1 = cell1
106+
, cell2 = cell2
107+
, cell3 = cell3
108+
}
109+
)
110+
(ListE.getAt 0 slots)
111+
(ListE.getAt 1 slots)
112+
(ListE.getAt 2 slots)
113+
(ListE.getAt 3 slots)
114+
(ListE.getAt 4 slots)
115+
|> Maybe.andThen selectBg
116+
|> Maybe.andThen selectTendrils
117+
|> Maybe.andThen selectCells

0 commit comments

Comments
 (0)