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

Add Hashvatars #298

Merged
merged 1 commit into from
Jan 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dependencies": {
"direct": {
"NoRedInk/elm-simple-fuzzy": "1.0.3",
"avh4/elm-color": "1.0.0",
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
Expand All @@ -24,6 +25,7 @@
"j-maas/elm-ordered-containers": "1.0.0",
"krisajenkins/remotedata": "6.0.1",
"mgold/elm-nonempty-list": "4.1.0",
"noahzgordon/elm-color-extra": "1.0.2",
"stoeffel/set-extra": "1.2.3",
"wernerdegroot/listzipper": "4.0.0"
},
Expand All @@ -33,12 +35,14 @@
"elm/random": "1.0.0",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2",
"fredcy/elm-parseint": "2.0.1",
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
}
},
"test-dependencies": {
"direct": {
"elm-explorations/test": "1.2.2"
"elm-explorations/test": "1.2.2",
"TSFoster/elm-tuple-extra": "2.0.0"
},
"indirect": {}
}
Expand Down
186 changes: 186 additions & 0 deletions src/Color/Harmony.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
module Color.Harmony exposing (..)

import Color exposing (Color)
import List.Extra as ListE


harmonizesWith : Color -> List Color
harmonizesWith color =
let
complementary_ =
complementary color

( analogousA, analogousB ) =
analogous color

( triadicA, triadicB ) =
triadic color

( splitA, splitB ) =
splitComplementary color

( squareA, squareB, squareC ) =
square color

( tetridicA, tetridicB, tetridicC ) =
tetridic color

harmonizesWith_ =
[ complementary_
, analogousA
, analogousB
, triadicA
, triadicB
, splitA
, splitB
, squareA
, squareB
, squareC
, tetridicA
, tetridicB
, tetridicC
]
in
ListE.uniqueBy Color.toCssString harmonizesWith_


{-| RGB Difference <https://en.wikipedia.org/wiki/Color_difference> - alpha is disregarded
-}
difference : Color -> Color -> Float
difference a b =
let
a_ =
toRgb255 a

b_ =
toRgb255 b

sum =
toFloat (((a_.red - b_.red) ^ 2) + ((a_.blue - b_.blue) ^ 2) + ((a_.green - b_.green) ^ 2))
in
sqrt sum


toRgb255 : Color -> { red : Int, green : Int, blue : Int }
toRgb255 c =
let
rgba =
Color.toRgba c
in
{ red = floor (rgba.red * 255)
, green = floor (rgba.red * 255)
, blue = floor (rgba.blue * 255)
}


{-| Opposites on the color wheel
-}
complementary : Color -> Color
complementary color =
hueAdd 180 color


{-| Adjacent colors on the color wheel
-}
analogous : Color -> ( Color, Color )
analogous color =
( hueAdd 30 color
, hueSubtract 30 color
)


triadic : Color -> ( Color, Color )
triadic color =
( hueAdd 120 color
, hueAdd 240 color
)


splitComplementary : Color -> ( Color, Color )
splitComplementary color =
( hueAdd 150 color
, hueAdd 210 color
)


square : Color -> ( Color, Color, Color )
square color =
( hueAdd 90 color
, hueAdd 180 color
, hueAdd 270 color
)


tetridic : Color -> ( Color, Color, Color )
tetridic color =
( hueAdd 60 color
, hueAdd 180 color
, hueAdd 240 color
)



-- Internal Helpers


hueAdd : Int -> Color -> Color
hueAdd num color =
let
hsla =
Color.toHsla color

hue =
hsla.hue
|> toAngle
|> (+) num
|> wrap360
|> toPt
in
Color.fromHsla { hsla | hue = hue }


hueSubtract : Int -> Color -> Color
hueSubtract num color =
let
hsla =
Color.toHsla color

hue =
hsla.hue
|> toAngle
|> (-) num
|> wrap360
|> toPt
in
Color.fromHsla { hsla | hue = hue }


toAngle : Float -> Int
toAngle pt =
let
a =
floor (pt * 360)
in
if a > 360 then
360 - (360 - a)

else
a


toPt : Int -> Float
toPt ang =
toFloat ang / 360


wrap360 : Int -> Int
wrap360 h =
let
x =
modBy 360 h
in
if x < 0 then
x + 360

else
x
117 changes: 117 additions & 0 deletions src/Hashvatar.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
module Hashvatar exposing (..)

import Hash exposing (Hash)
import Hashvatar.HexGrid as HexGrid
import Html exposing (Html, div)
import Html.Attributes exposing (class)
import List.Extra as ListE
import String.Extra exposing (break)
import UI.Color as Color exposing (Color)


view : Hash -> Html msg
view hash =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not the most efficient thing to do every time we want to render an avatar, but realistically we wont have hundreds on a page at the same time.

let
raw =
hash |> Hash.toString |> String.replace "#" ""

numSlots =
5

partLength =
String.length raw // numSlots

parts =
break partLength raw

toCharCodeSum str =
str
|> String.toList
|> List.foldl (\c acc -> acc + Char.toCode c) 0

grid =
parts
|> List.map toCharCodeSum
|> toGrid
|> Maybe.withDefault HexGrid.empty
in
div [ class "hashvatar" ]
[ HexGrid.view grid
]



-- Helpers


getIn : Int -> List Color -> Maybe Color
getIn unmoddedIdx colors_ =
ListE.getAt (modBy (List.length colors_) unmoddedIdx) colors_


toGrid : List Int -> Maybe HexGrid.HexGrid
toGrid slots =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function feels a bit gnarly. Its mostly due to having to deal with optionality when getting elements in the list by index, even though we know the size of the list with a guarantee.

let
selectBg grid_ =
let
background =
getIn grid_.background Color.darkNonGrays
in
Maybe.map
(\bg ->
{ background = bg
, tendrils = grid_.tendrils
, cell1 = grid_.cell1
, cell2 = grid_.cell2
, cell3 = grid_.cell3
}
)
background

selectTendrils grid_ =
let
tendrils =
getIn grid_.tendrils (Color.harmonizesWith grid_.background)
in
Maybe.map
(\tr ->
{ background = grid_.background
, tendrils = tr
, cell1 = grid_.cell1
, cell2 = grid_.cell2
, cell3 = grid_.cell3
}
)
tendrils

selectCells grid_ =
Maybe.map3
(\cell1 cell2 cell3 ->
{ background = grid_.background
, tendrils = grid_.tendrils
, cell1 = cell1
, cell2 = cell2
, cell3 = cell3
}
)
(getIn grid_.cell1 (Color.harmonizesWith grid_.background))
(getIn grid_.cell2 (Color.harmonizesWith grid_.background))
(getIn grid_.cell3 (Color.harmonizesWith grid_.background))
in
Maybe.map5
(\background tendrils cell1 cell2 cell3 ->
{ background = background
, tendrils = tendrils
, cell1 = cell1
, cell2 = cell2
, cell3 = cell3
}
)
(ListE.getAt 0 slots)
(ListE.getAt 1 slots)
(ListE.getAt 2 slots)
(ListE.getAt 3 slots)
(ListE.getAt 4 slots)
|> Maybe.andThen selectBg
|> Maybe.andThen selectTendrils
|> Maybe.andThen selectCells
Loading