Skip to content
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
14 changes: 7 additions & 7 deletions elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm-explorations/webgl": "1.1.3",
"ianmackenzie/elm-3d-camera": "3.1.0",
"ianmackenzie/elm-3d-scene": "1.0.2",
"ianmackenzie/elm-geometry": "3.11.0",
"ianmackenzie/elm-3d-camera": "4.0.1",
"ianmackenzie/elm-3d-scene": "1.1.0",
"ianmackenzie/elm-geometry": "4.0.0",
"ianmackenzie/elm-triangular-mesh": "1.1.0",
"ianmackenzie/elm-units": "2.10.0",
"mdgriffith/elm-animator": "1.1.1",
"w0rm/elm-obj-file": "1.2.1",
"w0rm/elm-physics": "5.1.3"
"w0rm/elm-obj-file": "1.4.0",
"w0rm/elm-physics": "6.0.1"
},
"indirect": {
"elm/bytes": "1.0.8",
Expand All @@ -31,9 +32,8 @@
"elm-explorations/linear-algebra": "1.0.3",
"ianmackenzie/elm-1d-parameter": "1.0.1",
"ianmackenzie/elm-float-extra": "1.1.0",
"ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.2",
"ianmackenzie/elm-geometry-linear-algebra-interop": "2.0.3",
"ianmackenzie/elm-interval": "3.1.0",
"ianmackenzie/elm-triangular-mesh": "1.1.0",
"ianmackenzie/elm-units-interval": "3.2.0"
}
},
Expand Down
55 changes: 23 additions & 32 deletions src/Ball.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ import Axis3d
import Bodies exposing (Id(..))
import Circle2d exposing (Circle2d)
import Color exposing (Color)
import Density
import Direction2d
import Direction3d
import EightBall exposing (Ball)
import Length exposing (Length, Meters)
import Mass exposing (Mass)
import Physics.Body as Body exposing (Body)
import Physics.Coordinates exposing (BodyCoordinates, WorldCoordinates)
import Physics.Material as Material exposing (Material)
import Physics.World as World exposing (World)
import Physics exposing (Body, BodyCoordinates, WorldCoordinates)
import Physics.Material as Material exposing (Dense, Material)
import Point2d exposing (Point2d)
import Point3d
import Quantity
Expand All @@ -31,11 +29,6 @@ radius =
Length.millimeters (57.15 / 2)


weight : Mass
weight =
Mass.grams 170


{-| The perfect sphere keeps rolling on the perfect plane,
we need to damp the angular velocity to make it stop.
-}
Expand All @@ -44,22 +37,22 @@ damping =
{ linear = 0.4, angular = 0.4 }


material : Material
material : Material Dense
material =
Material.custom
{ friction = 0.06
-- chosen to give a regulation 170 g ball at 57.15 mm diameter
Material.dense
{ density = Density.kilogramsPerCubicMeter 1739.43
, friction = 0.06
, bounciness = 0.6
}


body : id -> Body id
body id =
Body.sphere (Sphere3d.atOrigin radius) id
|> Body.withMaterial material
|> Body.withDamping damping
|> Body.withBehavior (Body.dynamic weight)
body : Body
body =
Physics.sphere (Sphere3d.atOrigin radius) material
|> Physics.damp damping
-- rotate to see the numbers on the balls
|> Body.rotateAround Axis3d.x (Angle.degrees 90)
|> Physics.rotateAround Axis3d.x (Angle.degrees 90)


entity : Material.Texture Color -> Material.Texture Float -> Entity BodyCoordinates
Expand All @@ -76,7 +69,7 @@ entity baseColor roughnessTexture =

{-| Rack the balls at the foot spot on the table
-}
rack : Point2d Meters WorldCoordinates -> List (Body Id)
rack : Point2d Meters WorldCoordinates -> List ( Id, Body )
rack footSpot =
let
-- TODO: randomly shuffle the balls?
Expand Down Expand Up @@ -117,15 +110,15 @@ rack footSpot =
)
|> List.filterMap
(\( maybeId, pos ) ->
Maybe.map (body >> Body.moveTo pos) maybeId
Maybe.map (\id -> ( id, body |> Physics.moveTo pos )) maybeId
)


{-| Place a spotted ball on the line behind the foot spot,
such that it doesn't collide with existing balls
-}
spot : Point2d Meters WorldCoordinates -> Ball -> World Id -> World Id
spot footSpot spottedBall world =
spot : Point2d Meters WorldCoordinates -> Ball -> List ( Id, Body ) -> List ( Id, Body )
spot footSpot spottedBall bodies =
let
-- the line behind the foot spot
axis =
Expand All @@ -147,16 +140,15 @@ spot footSpot spottedBall world =
-- a1 (f) a2 b1 b2 c1 c2 |
-- foot spot -----> foot rail
occupiedRanges =
world
|> World.bodies
bodies
|> List.filterMap
(\b ->
case Body.data b of
(\( id, b ) ->
case id of
Numbered _ ->
occupiedRange (Body.originPoint b)
occupiedRange (Physics.originPoint b)

CueBall ->
occupiedRange (Body.originPoint b)
occupiedRange (Physics.originPoint b)

_ ->
Nothing
Expand Down Expand Up @@ -200,8 +192,7 @@ spot footSpot spottedBall world =
|> Point3d.on SketchPlane3d.xy
|> Point3d.translateIn Direction3d.z radius
in
world
|> World.add (body (Numbered spottedBall) |> Body.moveTo spawnLocation)
( Numbered spottedBall, body |> Physics.moveTo spawnLocation ) :: bodies


intersectBy : Axis2d Meters coordinates -> Circle2d Meters coordinates -> Maybe ( Length, Length )
Expand Down
21 changes: 9 additions & 12 deletions src/Camera.elm
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import Animator exposing (Timeline)
import Axis3d exposing (Axis3d)
import Camera3d exposing (Camera3d)
import Length exposing (Meters)
import Physics.Coordinates exposing (WorldCoordinates)
import Physics exposing (WorldCoordinates)
import Pixels exposing (Pixels)
import Point2d exposing (Point2d)
import Point3d exposing (Point3d)
Expand All @@ -40,7 +40,6 @@ import Rectangle2d exposing (Rectangle2d)
import SketchPlane3d
import Time exposing (Posix)
import Vector2d
import Viewpoint3d


{-| Screen space coordinate system
Expand Down Expand Up @@ -91,16 +90,14 @@ camera3d (Camera camera) =
>> (\p -> { x = Animator.at p.x, y = Animator.at p.y, z = Animator.at p.z })
)
in
Camera3d.perspective
{ viewpoint =
Viewpoint3d.orbit
{ focalPoint = focalPoint
, groundPlane = SketchPlane3d.xy
, azimuth = camera.azimuth
, elevation = angleFromTimeline camera.elevation
, distance = distance
}
, verticalFieldOfView = Angle.degrees 24
Camera3d.orbit
{ focalPoint = focalPoint
, groundPlane = SketchPlane3d.xy
, azimuth = camera.azimuth
, elevation = angleFromTimeline camera.elevation
, distance = distance
, projection = Camera3d.Perspective
, fov = Camera3d.angle (Angle.degrees 24)
}


Expand Down
32 changes: 12 additions & 20 deletions src/Cue.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,12 @@ import Color exposing (Color)
import Cylinder3d exposing (Cylinder3d)
import Direction3d
import Length exposing (Length, Meters)
import Physics.Body as Body
import Physics.Coordinates exposing (WorldCoordinates)
import Physics.World as World exposing (World)
import Physics exposing (Body, WorldCoordinates)
import Plane3d
import Point3d
import Quantity
import Scene3d exposing (Entity)
import Scene3d.Material as Material
import SketchPlane3d
import Viewpoint3d


length : Length
Expand Down Expand Up @@ -66,10 +63,9 @@ cylinder : Camera3d Meters WorldCoordinates -> Length -> Axis3d Meters WorldCoor
cylinder camera3d clipDepth axis =
let
viewPlane =
camera3d
|> Camera3d.viewpoint
|> Viewpoint3d.viewPlane
|> SketchPlane3d.toPlane
Plane3d.through
(Camera3d.eyePoint camera3d)
(Camera3d.viewDirection camera3d)

trimmedCueLength =
-- shorten the cue cylinder if intersects with the view plane
Expand Down Expand Up @@ -99,8 +95,8 @@ cylinder camera3d clipDepth axis =

{-| Check if the cue doesn't overlap with any other objects
-}
canShoot : World Id -> Axis3d Meters WorldCoordinates -> Bool
canShoot world axis =
canShoot : List ( Id, Body ) -> Axis3d Meters WorldCoordinates -> Bool
canShoot bodies axis =
let
direction =
Axis3d.direction axis
Expand All @@ -114,8 +110,8 @@ canShoot world axis =
(Axis3d.originPoint axis)

-- ignore collision with the cue ball
worldWithoutCueBall =
World.keepIf (\b -> Body.data b /= CueBall) world
bodiesWithoutCueBall =
List.filter (\( id, _ ) -> id /= CueBall) bodies
in
-- cast 8 rays along the surface of the cue cylinder
List.all
Expand All @@ -128,15 +124,11 @@ canShoot world axis =
cueRay =
Axis3d.through rotatedPoint direction
in
case World.raycast cueRay worldWithoutCueBall of
Just { point, body } ->
let
collisionPoint =
point |> Point3d.placeIn (Body.frame body)
in
case Physics.raycast cueRay bodiesWithoutCueBall of
Just ( _, _, { point } ) ->
-- if the distance is greater than the cue length + offset, then there is no overlap
rotatedPoint
|> Point3d.distanceFrom collisionPoint
|> Point3d.distanceFrom point
|> Quantity.greaterThan (Quantity.plus offset length)

Nothing ->
Expand Down
Loading
Loading