Created
April 28, 2017 22:43
-
-
Save pbevin/64bf10d4190dcddaa1a92ac8cd38b610 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main exposing (..) | |
import Exts.Float exposing (roundTo) | |
import Html exposing (..) | |
import Html.Attributes exposing (..) | |
import Html.Events exposing (on) | |
import Json.Decode as Decode | |
import Mouse exposing (Position) | |
import Keyboard exposing (..) | |
import Char | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
-- MODEL | |
type alias Model = | |
{ origin : Mouse.Position | |
, mousePos : Maybe Mouse.Position | |
, l1 : Float | |
, l2 : Float | |
} | |
init : ( Model, Cmd Msg ) | |
init = | |
( Model { x = 400, y = 400 } Nothing 200 200, Cmd.none ) | |
-- UPDATE | |
type Msg | |
= TargetPos Mouse.Position | |
update : Msg -> Model -> ( Model, Cmd Msg ) | |
update msg model = | |
( updateHelp msg model, Cmd.none ) | |
updateHelp : Msg -> Model -> Model | |
updateHelp msg model = | |
case msg of | |
TargetPos pos -> | |
{ model | mousePos = Just pos } | |
-- SUBSCRIPTIONS | |
subscriptions : Model -> Sub Msg | |
subscriptions model = | |
Mouse.moves TargetPos | |
-- VIEW | |
(=>) = | |
(,) | |
view : Model -> Html Msg | |
view model = | |
case model.mousePos of | |
Nothing -> | |
div [] [] | |
Just pos -> | |
viewWithTargetPos pos model | |
viewWithTargetPos : Mouse.Position -> Model -> Html Msg | |
viewWithTargetPos { x, y } model = | |
let | |
( x1, y1 ) = | |
( toFloat model.origin.x, toFloat model.origin.y ) | |
( dx, dy ) = | |
( toFloat x - x1, toFloat y - y1 ) | |
( angle1, angle2 ) = | |
calcAngles model.l1 model.l2 ( dx, dy ) | |
( x2, y2 ) = | |
rotate ( x1, y1 ) angle1 model.l1 | |
( x3, y3 ) = | |
rotate ( x2, y2 ) (angle1 + angle2) model.l2 | |
isImpossible = | |
isNaN angle1 | |
in | |
div | |
[ style | |
[ "background-color" => "#555" | |
, "color" => "#fff" | |
, "cursor" => "move" | |
, "width" => "800px" | |
, "height" => "800px" | |
, "border-radius" => "4px" | |
, "position" => "relative" | |
, "display" => "flex" | |
, "align-items" => "center" | |
, "justify-content" => "center" | |
] | |
] | |
[ pre | |
[ style | |
[ "position" => "absolute" | |
, "left" => "0" | |
, "top" => "0" | |
] | |
] | |
[ text <| "Elbow is at (" ++ showFloat x2 ++ ", " ++ showFloat y2 ++ ")\n" | |
, text <| "Hand is at (" ++ showFloat x3 ++ ", " ++ showFloat y3 ++ ")\n" | |
, text <| "Target (" ++ toString x ++ ", " ++ toString y ++ ") => (" ++ showFloat (angle1 * 180 / pi) ++ "°, " ++ showFloat (angle2 * 180 / pi) ++ "°) => (" ++ showFloat x3 ++ ", " ++ showFloat y3 ++ ")\n" | |
, text <| | |
if isImpossible then | |
"IMPOSSIBLE!\n" | |
else | |
"\n" | |
] | |
, div | |
[ style | |
[ "position" => "absolute" | |
-- , "left" => px x1 | |
-- , "top" => px y1 | |
, "left" => px x1 | |
, "top" => px y1 | |
, "width" => px model.l1 | |
, "height" => "0px" | |
, "border-top" => "2px solid #3c8d2f" | |
, "border-bottom" => "2px solid #3c8d2f" | |
, "transform" => cssTransform [ cssRotate angle1 ] | |
, "transform-origin" => "0 50%" | |
] | |
] | |
[] | |
, div | |
[ style | |
[ "position" => "absolute" | |
, "left" => px x2 | |
, "top" => px y2 | |
, "width" => px model.l2 | |
, "height" => "0px" | |
, "border-top" => "2px solid #2f6a8d" | |
, "border-bottom" => "2px solid #2f6a8d" | |
, "transform" => cssTransform [ cssRotate (angle1 + angle2) ] | |
, "transform-origin" => "0 50%" | |
] | |
] | |
[] | |
, div | |
[ style | |
[ "position" => "absolute" | |
, "left" => px (x3 - 10) | |
, "top" => px (y3 - 10) | |
, "width" => "20px" | |
, "height" => "20px" | |
, "border-radius" => "10px" | |
, "background-color" => "#8d2f2f" | |
, "transform" => cssTransform [ cssRotate (angle1 + angle2) ] | |
, "transform-origin" => "50% 50%" | |
] | |
] | |
[] | |
] | |
cssTransform : List String -> String | |
cssTransform = | |
String.join " " << List.reverse | |
cssTranslate : Float -> Float -> String | |
cssTranslate x y = | |
"translate(" ++ toString x ++ "px, " ++ toString y ++ "px)" | |
cssRotate : Float -> String | |
cssRotate angle = | |
"rotate(" ++ toString angle ++ "rad)" | |
px : Float -> String | |
px number = | |
toString number ++ "px" | |
type alias Pos = | |
( Float, Float ) | |
type alias Angle = | |
Float | |
type alias Length = | |
Float | |
rotate : Pos -> Angle -> Length -> Pos | |
rotate ( x0, y0 ) angle len = | |
let | |
dx = | |
len * cos angle | |
dy = | |
len * sin angle | |
in | |
( x0 + dx, y0 + dy ) | |
showFloat : Float -> String | |
showFloat = | |
toString << roundTo 2 | |
calcAngles : Length -> Length -> Pos -> ( Angle, Angle ) | |
calcAngles l1 l2 ( x, y ) = | |
let | |
aa = | |
sqrt (2 * l1 * l1 * x * x + 2 * l2 * l2 * x * x + 2 * l1 * l1 * y * y + 2 * l2 * l2 * y * y - l1 * l1 * l1 * l1 - l2 * l2 * l2 * l2 + 2 * l1 * l1 * l2 * l2 - x * x * x * x - 2 * x * x * y * y - y * y * y * y) | |
d1 = | |
2 * l1 * x + l1 * l1 - l2 * l2 + x * x + y * y | |
d2 = | |
2 * l2 * x - l1 * l1 + l2 * l2 + x * x + y * y | |
a = | |
2 * atan ((aa + 2 * l1 * y) / d1) | |
b = | |
2 * atan ((2 * l2 * y - aa) / d2) | |
in | |
( a, b - a ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment