-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3d color & lighting, more solids, Angle utilities #166
Changes from 11 commits
2594195
d1806e4
26807c2
44bdbad
7ff0263
216317f
03833af
2e03dec
1229c55
ec8f0a9
9148c6a
debf974
78c0522
a52e8e5
e4312f0
3ebb80d
daca572
d7a09ea
7b356a7
e1897ca
49d298f
77480c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
{-# LANGUAGE DeriveDataTypeable #-} | ||
{-# LANGUAGE ExistentialQuantification #-} | ||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
{-# LANGUAGE TemplateHaskell #-} | ||
----------------------------------------------------------------------------- | ||
-- | | ||
-- Module : Diagrams.ThreeD.Attributes | ||
-- Copyright : (c) 2014 diagrams-lib team (see LICENSE) | ||
-- License : BSD-style (see LICENSE) | ||
-- Maintainer : diagrams-discuss@googlegroups.com | ||
-- | ||
-- Diagrams may have /attributes/ which affect the way they are | ||
-- rendered. This module defines some common attributes relevant in | ||
-- 3D; particular backends may also define more backend-specific | ||
-- attributes. | ||
-- | ||
-- Every attribute type must have a /semigroup/ structure, that is, an | ||
-- associative binary operation for combining two attributes into one. | ||
-- Unless otherwise noted, all the attributes defined here use the | ||
-- 'Last' structure, that is, combining two attributes simply keeps | ||
-- the second one and throws away the first. This means that child | ||
-- attributes always override parent attributes. | ||
-- | ||
----------------------------------------------------------------------------- | ||
|
||
module Diagrams.ThreeD.Attributes where | ||
|
||
import Control.Lens | ||
import Data.Semigroup | ||
import Data.Typeable | ||
|
||
import Data.Colour | ||
|
||
import Diagrams.Core | ||
|
||
-- | @SurfaceColor@ is the inherent pigment of an object, assumed to | ||
-- be opaque. | ||
newtype SurfaceColor = SurfaceColor (Last (Colour Double)) | ||
deriving (Typeable, Semigroup) | ||
instance AttributeClass SurfaceColor | ||
|
||
surfaceColor :: Iso' SurfaceColor (Colour Double) | ||
surfaceColor = iso (\(SurfaceColor (Last c)) -> c) (SurfaceColor . Last) | ||
|
||
sc :: HasStyle d => Colour Double -> d -> d | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a Haddock comment for |
||
sc = applyAttr . review surfaceColor | ||
|
||
-- | @Diffuse@ is the fraction of incident light reflected diffusely, | ||
-- that is, in all directions. The actual light reflected is the | ||
-- product of this value, the incident light, and the @SurfaceColor@ | ||
-- Attribute. For physical reasonableness, @Diffuse@ should have a | ||
-- value between 0 and 1; this is not checked. | ||
newtype Diffuse = Diffuse (Last Double) | ||
deriving (Typeable, Semigroup) | ||
instance AttributeClass Diffuse | ||
|
||
_Diffuse :: Iso' Diffuse Double | ||
_Diffuse = iso (\(Diffuse (Last d)) -> d) (Diffuse . Last) | ||
|
||
diffuse :: HasStyle d => Double -> d -> d | ||
diffuse = applyAttr . review _Diffuse | ||
|
||
-- | @Ambient@ is an ad-hoc representation of indirect lighting. The | ||
-- product of @Ambient@ and @SurfaceColor@ is added to the light | ||
-- leaving an object due to diffuse and specular terms. @Ambient@ can | ||
-- be set per-object, and can be loosely thought of as the product of | ||
-- indirect lighting incident on that object and the diffuse | ||
-- reflectance. | ||
newtype Ambient = Ambient (Last Double) | ||
deriving (Typeable, Semigroup) | ||
instance AttributeClass Ambient | ||
|
||
_Ambient :: Iso' Ambient Double | ||
_Ambient = iso (\(Ambient (Last d)) -> d) (Ambient . Last) | ||
|
||
ambient :: HasStyle d => Double -> d -> d | ||
ambient = applyAttr . review _Ambient | ||
|
||
-- | A specular highlight has two terms, the intensity, between 0 and | ||
-- 1, and the size. The highlight size is assumed to be the exponent | ||
-- in a Phong shading model (though Backends are free to use a | ||
-- different shading model). In this model, reasonable values are | ||
-- between 1 and 50 or so, with higher values for shinier objects. | ||
-- Physically, the intensity and the value of @Diffuse@ must add up to | ||
-- less than 1; this is not enforced. | ||
data Specular = Specular { _specularIntensity :: Double | ||
, _specularSize :: Double | ||
} | ||
|
||
makeLenses ''Specular | ||
|
||
newtype Highlight = Highlight (Last Specular) | ||
deriving (Typeable, Semigroup) | ||
instance AttributeClass Highlight | ||
|
||
_Highlight :: Iso' Highlight Specular | ||
_Highlight = iso (\(Highlight (Last s)) -> s) (Highlight . Last) | ||
|
||
highlight :: HasStyle d => Specular -> d -> d | ||
highlight = applyAttr . review _Highlight |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,18 +15,24 @@ | |
|
||
module Diagrams.ThreeD.Shapes | ||
( | ||
Ellipsoid(..) | ||
, sphere | ||
Ellipsoid(..), sphere | ||
, Box(..), cube | ||
, Frustum(..) , frustum, cone, cylinder | ||
) where | ||
|
||
import Data.Typeable | ||
import Control.Applicative | ||
import Control.Lens ((^.), review, _1) | ||
|
||
import Data.AffineSpace | ||
import Data.Semigroup | ||
import Data.VectorSpace | ||
import Diagrams.Coordinates | ||
import Diagrams.Core | ||
import Diagrams.Solve | ||
import Diagrams.ThreeD.Types | ||
import Diagrams.ThreeD.Vector | ||
import Diagrams.TwoD.Types | ||
|
||
data Ellipsoid = Ellipsoid T3 | ||
deriving Typeable | ||
|
@@ -41,6 +47,7 @@ instance IsPrim Ellipsoid | |
instance Renderable Ellipsoid NullBackend where | ||
render _ _ = mempty | ||
|
||
-- | A sphere of radius 1 with its center at the origin. | ||
sphere :: (Backend b R3, Renderable Ellipsoid b) => Diagram b R3 | ||
sphere = mkQD (Prim $ Ellipsoid mempty) | ||
(mkEnvelope sphereEnv) | ||
|
@@ -54,3 +61,108 @@ sphere = mkQD (Prim $ Ellipsoid mempty) | |
c = p' <.> p' - 1 | ||
p' = p .-. origin | ||
sphereQuery v = Any $ magnitudeSq (v .-. origin) <= 1 | ||
|
||
data Box = Box T3 | ||
deriving (Typeable) | ||
|
||
type instance V Box = R3 | ||
|
||
instance Transformable Box where | ||
transform t1 (Box t2) = Box (t1 <> t2) | ||
|
||
instance IsPrim Box | ||
|
||
instance Renderable Box NullBackend where | ||
render _ _ = mempty | ||
|
||
-- | A cube with side length 1, in the positive octant, with one | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be really cool if we can eventually figure out a way to embed 3D illustrations here with diagrams-haddock. =) |
||
-- vertex at the origin. | ||
cube :: (Backend b R3, Renderable Box b) => Diagram b R3 | ||
cube = mkQD (Prim $ Box mempty) | ||
(mkEnvelope boxEnv) | ||
(mkTrace boxTrace) | ||
mempty | ||
(Query boxQuery) | ||
where | ||
corners = mkR3 <$> [0,1] <*> [0,1] <*> [0,1] | ||
boxEnv v = maximum (map (v <.>) corners) / magnitudeSq v | ||
-- ts gives all intersections with the planes forming the box | ||
-- filter keeps only those actually on the box surface | ||
boxTrace p v = mkSortedList . filter (range . atT) $ ts where | ||
(x0, y0, z0) = unp3 p | ||
(vx, vy, vz) = unr3 v | ||
intersections f d = case d of | ||
0 -> [] | ||
_ -> [-f/d, (1-f)/d] | ||
ts = concat $ zipWith intersections [x0,y0,z0] [vx,vy,vz] | ||
atT t = p .+^ (t*^v) | ||
range u = and [x >= 0, x <= 1, y >= 0, y <= 1, z >= 0, z <= 1] where | ||
(x, y, z) = unp3 u | ||
boxQuery = Any . range | ||
|
||
data Frustum = Frustum Double Double T3 | ||
deriving (Typeable) | ||
|
||
type instance V Frustum = R3 | ||
|
||
instance Transformable Frustum where | ||
transform t1 (Frustum r0 r1 t2) = Frustum r0 r1 (t1 <> t2) | ||
|
||
instance IsPrim Frustum | ||
|
||
instance Renderable Frustum NullBackend where | ||
render _ _ = mempty | ||
|
||
-- | A frustum of a right circular cone. It has height 1 oriented | ||
-- along the positive z axis, and radii r0 and r1 at Z=0 and Z=1. | ||
-- 'cone' and 'cylinder' are special cases. | ||
frustum :: (Backend b R3, Renderable Frustum b) => Double -> Double -> Diagram b R3 | ||
frustum r0 r1 = mkQD (Prim $ Frustum r0 r1 mempty) | ||
(mkEnvelope frEnv) | ||
(mkTrace frTrace) | ||
mempty | ||
(Query frQuery) | ||
where | ||
projectXY u = u ^-^ project unitZ u | ||
frQuery p = Any $ x >= 0 && x <= 1 && a <= r where | ||
(x, _, z) = unp3 p | ||
r = r0 + (r1-r0)*z | ||
v = p .-. origin | ||
a = magnitude $ projectXY v | ||
-- The plane containing v and the z axis intersects the frustum in a trapezoid | ||
-- Test the four corners of this trapezoid; one must determine the Envelope | ||
frEnv v = maximum . map (magnitude . project v . review cylindrical) $ corners | ||
where | ||
θ = v^._theta | ||
corners = [(r1,θ,1), (-r1,θ,1), (r0,θ,0), (-r0,θ,0)] | ||
-- The trace can intersect the sides of the cone or one of the end | ||
-- caps The sides are described by a quadric equation; substitute | ||
-- in the parametric form of the ray but disregard any | ||
-- intersections outside z = [0,1] Similarly, find intersections | ||
-- with the planes z=0, z=1, but disregard any r>r0, r>r1 | ||
frTrace p v = mkSortedList $ filter zbounds (quadForm a b c) ++ ends | ||
where | ||
(px, py, pz) = unp3 p | ||
(vx, vy, vz) = unr3 v | ||
ray t = p .+^ t*^v | ||
dr = r1-r0 | ||
a = vx**2 + vy**2 - vz**2 * dr**2 | ||
b = 2 * (px * vx + py * vy - (r0+pz*dr) * dr * vz) | ||
c = px**2 + py**2 - (r0 + dr*pz)**2 | ||
zbounds t = (ray t)^._z >= 0 && (ray t)^._z <= 1 | ||
ends = concatMap cap [0,1] | ||
cap z = if (ray t)^.cylindrical._1 < r0 + z*dr | ||
then [t] | ||
else [] | ||
where | ||
t = (z - pz) / vz | ||
|
||
-- | A cone with its base centered on the origin, with radius 1 at the | ||
-- base, height 1, and it's apex on the positive Z axis. | ||
cone :: (Backend b R3, Renderable Frustum b) => Diagram b R3 | ||
cone = frustum 1 0 | ||
|
||
-- | A circular cylinder of radius 1 with one end cap centered on the | ||
-- origin, and extending to Z=1. | ||
cylinder :: (Backend b R3, Renderable Frustum b) => Diagram b R3 | ||
cylinder = frustum 1 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a better comment for this? The vector magnitude of what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, that is a terrible comment. I will write something better in the morning.