we can now dictate area and opacity as a fn of nth triangle generated
This commit is contained in:
parent
80c096a65c
commit
059042a354
2 changed files with 37 additions and 70 deletions
74
src/Main.hs
74
src/Main.hs
|
|
@ -10,6 +10,7 @@ import qualified Graphics.Image.ColorSpace as G
|
|||
import qualified Data.Colour.SRGB as C
|
||||
import Diagrams.Backend.SVG.CmdLine
|
||||
import Diagrams.Prelude
|
||||
import Debug.Trace
|
||||
import Data.List
|
||||
import Control.Parallel.Strategies
|
||||
import qualified Graphics.Image.Interface.Repa as Img
|
||||
|
|
@ -18,19 +19,16 @@ import qualified Data.Vector as Vec
|
|||
import qualified Debug.Trace as DT
|
||||
|
||||
data Options = Options {
|
||||
nRounds :: Int,
|
||||
nTrianglesPerRound :: Int,
|
||||
numTriangles :: Int,
|
||||
gen :: Maybe StdGen
|
||||
}
|
||||
|
||||
-- modify this to your liking
|
||||
defaultOpts = Options {
|
||||
nRounds = 3,
|
||||
nTrianglesPerRound = 100,
|
||||
numTriangles = 3000,
|
||||
gen = Nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
genList :: StdGen -> [StdGen]
|
||||
genList = map mkStdGen . randoms
|
||||
|
||||
|
|
@ -40,53 +38,37 @@ tosRGB' (G.PixelRGB r g b) = C.sRGB r g b
|
|||
convImage :: Image VU G.RGB Double -> Vec.Vector (Colour Double)
|
||||
convImage = Vec.map tosRGB' . Vec.convert . R.toUnboxed . Img.toRepaArray
|
||||
|
||||
renderTriangles
|
||||
:: Vec.Vector (Colour Double)
|
||||
-> Int
|
||||
-> (Int, Int)
|
||||
-> Double
|
||||
-> Int
|
||||
-> StdGen
|
||||
-> Int
|
||||
-> QDiagram SVG V2 Double Any
|
||||
renderTriangles image nRounds dimensions areaCoeff nTrianglesPerRound gen round'
|
||||
= mconcat . map renderTriangle $ triangles
|
||||
-- progress goes from 0 to 1 the farther we get along the process
|
||||
renderTri :: Vec.Vector (Colour Double) -> (Int, Int) -> StdGen -> Double -> QDiagram SVG V2 Double Any
|
||||
renderTri image dimensions gen progress = Ren.makeTriangle (Ren.toPointList dimensions triangle) color opacity'
|
||||
where
|
||||
renderTriangle t = reflectY $ Ren.makeTriangle (Ren.toPointList dimensions t) col opacity
|
||||
where
|
||||
col :: C.Colour Double
|
||||
col = Tri.getTriangleAverageRGB image t dimensions
|
||||
|
||||
numCandidates = round $ (fromIntegral nTrianglesPerRound) / areaCoeff
|
||||
|
||||
opacity :: Double
|
||||
opacity = fromIntegral round' / fromIntegral nRounds
|
||||
|
||||
area :: Maybe Double
|
||||
area = Just $ (\y -> DT.traceShow (round', y) y) $ 1 - (x + 0.1)
|
||||
where
|
||||
-- 0.000, 0.333, 0.666
|
||||
x = fromIntegral (round' - 1) / fromIntegral nRounds
|
||||
triangle = Tri.getRandomTriangle dimensions (Just area) gen
|
||||
|
||||
triangles
|
||||
= take nTrianglesPerRound
|
||||
$ sortOn Tri.area
|
||||
. take numCandidates
|
||||
. filter (not . Tri.sharesCoords)
|
||||
. map (Tri.getRandomTriangle dimensions area)
|
||||
. genList
|
||||
$ gen
|
||||
color = Tri.getTriangleAverageRGB image triangle dimensions
|
||||
|
||||
-- the following should be considered triangle shaders
|
||||
-- modify them to your liking, their outputs are expected to be in [0, 1]
|
||||
|
||||
opacity' = 0.1 + (progress) * 0.8
|
||||
|
||||
area = 0.05 + (1 - progress) * 0.2
|
||||
|
||||
genImage :: String -> Int -> Int -> Double -> IO (Diagram B)
|
||||
genImage name areaCoeff = do
|
||||
let
|
||||
|
||||
|
||||
|
||||
genImage :: String -> IO (Diagram B)
|
||||
genImage name = do
|
||||
let (Options {numTriangles = numTriangles, gen = gen'}) = defaultOpts
|
||||
gen'' <- case gen' of
|
||||
Nothing -> getStdGen
|
||||
Just a -> return a
|
||||
image <- Img.readImageRGB VU name
|
||||
let img' = convImage image
|
||||
let dimensions = (rows image, cols image)
|
||||
let renderTriangles' = renderTriangles img' nRounds dimensions areaCoeff nTrianglesPerRound
|
||||
gen <- if randSeed == 0 then getStdGen else return $ mkStdGen randSeed
|
||||
let triangles = zipWith renderTriangles' (genList gen) $ [1..nRounds]
|
||||
return $ center . mconcat . withStrategy (parListChunk 800 rseq) $ reverse triangles
|
||||
print gen''
|
||||
let progressList = map (/ (fromIntegral numTriangles)) [0.0 .. (fromIntegral numTriangles)]
|
||||
return $ center . reflectY . mconcat . withStrategy (parListChunk 75 rseq) $ zipWith (renderTri img' dimensions) (genList gen'') progressList
|
||||
|
||||
main :: IO ()
|
||||
main = mainWith genImage
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ type Pixel_ = C.Colour Double
|
|||
type Point = (Int, Int)
|
||||
type Triangle = (Point, Point, Point)
|
||||
|
||||
sharesCoords :: Triangle -> Bool
|
||||
sharesCoords ((x1, y1), (x2, y2), (x3, y3)) = ((/= 3) . length . nub $ [x1, x2, x3])
|
||||
|| ((/= 3) . length . nub $ [y1, y2, y3])
|
||||
|| ((/= 3) . length . nub $ [y1, y2, y3])
|
||||
|
||||
shoelace :: [Point] -> Double
|
||||
shoelace pts = halve . sum $ zipWith (*) (zipWith (+) xs' xs) (zipWith (-) ys' ys)
|
||||
|
|
@ -40,7 +41,6 @@ shoelace' [(y1, x1), (y2, x2), (y3, x3)] = abs $ (* 0.5) . fromIntegral $ x1*y2
|
|||
|
||||
area :: Triangle -> Double
|
||||
area (p1, p2, p3) = shoelace' $ [p1, p2, p3]
|
||||
-- area (p1, p2, p3) = abs . ccArea . swapForCounterClockwise . sortOn fst $ [p1, p2, p3]
|
||||
where
|
||||
swapForCounterClockwise [a, b, c] = if snd a < snd b
|
||||
then [a, b, c]
|
||||
|
|
@ -59,18 +59,18 @@ getRandomPixel gen (rows, cols) =
|
|||
|
||||
gen' = snd . next $ gen
|
||||
|
||||
first3 :: [a] -> (a, a, a)
|
||||
first3 (a : b : c : _) = (a, b, c)
|
||||
|
||||
getP2 :: StdGen -> (Int, Int) -> Double -> (Int, Int)
|
||||
getP2 gen (x0, y0) r = (x0 + x, y0 + y)
|
||||
getP2 gen (x0, y0) r' = (x0 + x, y0 + y)
|
||||
where
|
||||
r = max 2.0 r'
|
||||
phi = fst . randomR (0.0, pi * 2) $ gen
|
||||
x = round $ r * cos phi
|
||||
y = round $ r * sin phi
|
||||
|
||||
|
||||
|
||||
|
||||
getRandomTriangle :: (Int, Int) -> Maybe Double -> StdGen -> Triangle
|
||||
getRandomTriangle dims area gen = (p1, p2, p3)
|
||||
where
|
||||
|
|
@ -85,14 +85,6 @@ getRandomTriangle dims area gen = (p1, p2, p3)
|
|||
|
||||
p3 = getThirdPoint p1 p2 gen0 (pi / 10.0)
|
||||
|
||||
-- y = mx + b
|
||||
-- y = m'x + b'
|
||||
|
||||
-- m'x + b' = mx + b
|
||||
-- m'x - mx = b - b'
|
||||
-- x(m' - m) = b - b'
|
||||
-- x = (b - b') / (m' - m)
|
||||
|
||||
angleIntersect :: (Point, Double) -> (Point, Double) -> Point
|
||||
angleIntersect ((y1, x1), angle1) ((y2, x2), angle2) = (round y3, round x3)
|
||||
where
|
||||
|
|
@ -153,12 +145,6 @@ getPointsInTriangle image (p1', p2', p3') = (ptsBtween (makeLine p1 p3) (makeLin
|
|||
p2 = sortedPoints !! 1
|
||||
p3 = sortedPoints !! 2
|
||||
|
||||
-- getPointsInTriangle image triangle =
|
||||
-- = filter (isPointInTriangle triangle)
|
||||
-- $ (,) <$> [0..(rows image)] <*> [0..(cols image)]
|
||||
|
||||
|
||||
|
||||
blendEqually colors = C.affineCombo (zip (repeat fraction) colors) $ C.black
|
||||
where
|
||||
fraction = 1.0 / (fromIntegral . length $ colors)
|
||||
|
|
@ -182,18 +168,17 @@ getTriangleAverageRGB image triangle (y', x') = blendEqually $ pixels
|
|||
| otherwise = image Vec.!? ((y * x') + x)
|
||||
|
||||
|
||||
-- pointsInTriangle image (p1, p2, p3) =
|
||||
-- where
|
||||
-- sortedPts = sortOn snd [p1, p2, p3]
|
||||
|
||||
ptsBtween :: Line -> Line -> [Point]
|
||||
ptsBtween l1 l2 = concatMap rasterLine [startingX .. endingX]
|
||||
ptsBtween l1 l2 = concatMap rasterLine . noSingletons $ [startingX .. endingX]
|
||||
where
|
||||
startingX = max (startX l1) (startX l2)
|
||||
endingX = min (endX l1) (endX l2)
|
||||
|
||||
rasterLine x = map (\y -> (y, x)) $ range' (yAt l1 x) (yAt l2 x)
|
||||
|
||||
noSingletons :: [a] -> [a]
|
||||
noSingletons [x] = []
|
||||
noSingletons l = l
|
||||
|
||||
range' :: Int -> Int -> [Int]
|
||||
range' a b = [(min a b) .. (max a b)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue