Skip to content

Commit

Permalink
optimize and document
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianHicks committed Feb 19, 2020
1 parent e9fe1c9 commit 73d263e
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 47 deletions.
98 changes: 55 additions & 43 deletions src/Constraint.elm
@@ -1,15 +1,27 @@
module Constraint exposing (Model, focusOn, init, positions, unfocus, updateAttachments)
module Constraint exposing (Model, focusOn, init, positions, unfocus, updateIdealPositions)

import Dict exposing (Dict)


{-| Lay out comments according to these constraints:
1. if there is a focused comment, it must be at it's ideal position
2. comments may not overlap
3. comments should be as close as possible to their ideal position (but the
first comment in an otherwise-overlapping sequence should be at it's ideal position)
Notably missing here: the ability to add new comments. That's just not something
that we need to solve for yet. Once we do, it should not be too hard to add
(recalculate the `comments` ordering with respect to the new height and ideal
position data, and re-solve.)
-}
type Model
= Model
{ -- comment ID to comment height
heights : Dict Int Float

-- comment ID to ideal position
, attachments : Dict Int Float
{ -- comment ID to dimensions. This needs to be sorted by idealPosition
-- since this algorithm will be walking the data that way several
-- times.
comments : List ( Int, { height : Float, idealPosition : Float } )

-- comment ID to actual position
, positions : Dict Int Float
Expand All @@ -24,14 +36,23 @@ type Model

init :
{ heights : Dict Int Float
, attachments : Dict Int Float
, idealPositions : Dict Int Float
, margin : Float
}
-> Model
init { heights, attachments, margin } =
init { heights, idealPositions, margin } =
Model
{ heights = heights
, attachments = attachments
{ comments =
-- take the intersection of the heights and ideal positions. Gotta have both for a comment to do this algorithm!
Dict.merge
(\_ _ result -> result)
(\key height idealPosition -> Dict.insert key { height = height, idealPosition = idealPosition })
(\_ _ result -> result)
heights
idealPositions
Dict.empty
|> Dict.toList
|> List.sortBy (\( _, { idealPosition } ) -> idealPosition)
, positions = Dict.empty
, margin = margin
, focus = Nothing
Expand All @@ -53,16 +74,9 @@ solveWithoutFocus (Model guts) =
Model
{ guts
| positions =
guts.attachments
|> Dict.toList
|> List.sortBy Tuple.second
guts.comments
|> List.foldl
(\( id, idealPosition ) ( finalPositions, progressLine ) ->
let
height =
Dict.get id guts.heights
|> Maybe.withDefault 0
in
(\( id, { idealPosition, height } ) ( finalPositions, progressLine ) ->
if idealPosition >= progressLine then
( Dict.insert id idealPosition finalPositions
, idealPosition + height + guts.margin
Expand All @@ -87,20 +101,13 @@ solveWithFocus (Model guts) =
solveWithoutFocus (Model guts)

( goUp, goDown ) =
newGuts.attachments
|> Dict.toList
|> List.sortBy Tuple.second
-- == 0 is a trick to get the compiler to generate
-- more efficient code. Will not always be needed!
|> splitAtReversing (\( curId, _ ) -> curId - id == 0)
-- == 0 is a trick to get the compiler to generate
-- more efficient code. Will not always be needed!
splitAtReversing (\( curId, _ ) -> curId - id == 0) newGuts.comments

( downwardPositions, _ ) =
List.foldl
(\( curId, idealPosition ) ( finalPositions, progressLine ) ->
let
height =
Dict.get curId newGuts.heights |> Maybe.withDefault 0
in
(\( curId, { height, idealPosition } ) ( finalPositions, progressLine ) ->
if idealPosition >= progressLine then
( Dict.insert curId idealPosition finalPositions
, idealPosition + height + newGuts.margin
Expand All @@ -116,11 +123,8 @@ solveWithFocus (Model guts) =

( downwardAndUpwardPositions, _ ) =
List.foldl
(\( curId, idealPosition ) ( finalPositions, progressLine ) ->
(\( curId, { height, idealPosition } ) ( finalPositions, progressLine ) ->
let
height =
Dict.get curId newGuts.heights |> Maybe.withDefault 0

currentPosition =
Dict.get curId newGuts.positions |> Maybe.withDefault idealPosition
in
Expand All @@ -140,8 +144,8 @@ solveWithFocus (Model guts) =
)
( downwardPositions
, case goDown of
( _, start ) :: _ ->
start
( _, { idealPosition } ) :: _ ->
idealPosition

_ ->
-- infinity
Expand All @@ -155,13 +159,21 @@ solveWithFocus (Model guts) =
solveWithFocus (Model guts)


updateAttachments : Dict Int Float -> Model -> Model
updateAttachments attachments (Model guts) =
if attachments == guts.attachments then
Model guts

else
Model { guts | attachments = attachments } |> solve
updateIdealPositions : Dict Int Float -> Model -> Model
updateIdealPositions attachments (Model guts) =
Model
{ guts
| comments =
guts.comments
|> List.filterMap
(\( id, metrics ) ->
Maybe.map
(\newIdealPosition -> ( id, { metrics | idealPosition = newIdealPosition } ))
(Dict.get id attachments)
)
|> List.sortBy (\( _, { idealPosition } ) -> idealPosition)
}
|> solve


focusOn : Int -> Model -> Model
Expand Down
8 changes: 4 additions & 4 deletions src/Main.elm
Expand Up @@ -97,7 +97,7 @@ update msg model =
( { model
| commentPositions =
Maybe.map
(Constraint.updateAttachments (finalAttachments attachments))
(Constraint.updateIdealPositions (idealPositionsFor attachments))
model.commentPositions
}
, Cmd.none
Expand All @@ -109,7 +109,7 @@ update msg model =
Just <|
Constraint.init
{ heights = Dict.fromList commentHeights
, attachments = finalAttachments attachments
, idealPositions = idealPositionsFor attachments
, margin = 10
}
}
Expand All @@ -133,8 +133,8 @@ update msg model =
)


finalAttachments : List ( Int, Float ) -> Dict Int Float
finalAttachments =
idealPositionsFor : List ( Int, Float ) -> Dict Int Float
idealPositionsFor =
List.foldl
(\( commentId, top ) soFar ->
Dict.update commentId
Expand Down

0 comments on commit 73d263e

Please sign in to comment.