we can now dictate area and opacity as a fn of nth triangle generated

This commit is contained in:
Jack Wines 2019-01-05 01:13:31 -05:00
parent 80c096a65c
commit 059042a354
2 changed files with 37 additions and 70 deletions

View file

@ -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

View file

@ -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)]