Skip to content
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

Gradient along the spline? #334

Open
ghost opened this issue Mar 25, 2019 · 12 comments
Open

Gradient along the spline? #334

ghost opened this issue Mar 25, 2019 · 12 comments

Comments

@ghost
Copy link

ghost commented Mar 25, 2019

Is there any general way to specify gradient along the 1d position on spline?
For example, this one I made using cubicSpline, it consists of 6 points:
https://imgur.com/MOqOKDN

Gradient is:
linear = mkLinearGradient (mkStops [(black, 0, 1), (white, 1, 1)]) 0 1 GradPad

What I want to achieve is that spline gradually becomes white along the way from the start to the end, so that point A is visible and B is not.

@fryguybob
Copy link
Member

I don't think this is typically supported by graphics packages and I think it is because it can be computationally a bit expensive and maybe difficult to get reliable results. That said, I think diagrams would be a great place to include this feature, though I'm not sure the best way to express it or if it could be supported by all backends. Here is an example of someone achieving this with SVG and JavaScript: https://bl.ocks.org/mbostock/4163057

@ghost
Copy link
Author

ghost commented Mar 25, 2019

@fryguybob
I've also been thinking about working around by dividing into a lot of small segments and interpolating their colors, I'm surprised this approach looks so good. I just tried it with diagrams and the immediate problem is that I can't find any way to sample points on spline. There are trailPoints and trailVertices, but I can't specify some delta or number of points I want to get.

@ghost
Copy link
Author

ghost commented Mar 25, 2019

There is a way to sample points using tracing like in showTrace':
https://hackage.haskell.org/package/diagrams-lib-1.4.2.3/docs/src/Diagrams.TwoD.Model.html#showTrace%27
But the problem is that points are out of order and they are not equally distributed on the spline.

@fryguybob
Copy link
Member

@ivxvm I think you want atParam and arcLengthToParam both from Diagrams.Parametric.

@ghost
Copy link
Author

ghost commented Mar 26, 2019

I've implemented similar approach with diagrams and I'm very satisfied with results. It allows not only gradients, but variation of any style parameters along the trails. Here's an example of decreasing-width-decreasing-opacity:
https://imgur.com/GZRmteT

Code is a mess, can be improved for sure:

morphingSpline :: Int -> (Double -> _ -> _) -> [P2 Double] -> Diagram B
morphingSpline numIterations styleFn points =
    mconcat $ map render $ getZipList $ (,,) <$> ZipList points' <*> ZipList (tail points') <*> ZipList ticks
  where
    spline  = cubicSpline False points :: Trail _ _
    points' = map (\t -> spline `atParam` t) ticks
    ticks   = [0 :: Double, 1 / fromIntegral numIterations .. 1]
    render (s, e, t) = strokeLocTrail (fromSegments [straight (e - s)] `at` P s) # styleFn t

styleFn t = opacity (1 - t) . lw (local ((1 - t) / 3))

diagram = morphingSpline 1000 styleFn points # bgFrame 1 white

Ripple texture is a side effect of aliasing (or something else), gonna figure out how to fix it.
Not an issue with default line width:
https://imgur.com/ym7oJyG

@bacchanalia
Copy link

bacchanalia commented Mar 26, 2019

You can simplify your code by using section. I was hoping it might also help with the ripple effect, but it doesn't.

edit: you can fix the ripple texture by overlapping segments (tail became drop 2, fixed below)

morphingSpline :: _ => Int -> (Double -> _ -> _) -> [P2 Double] -> Diagram b
morphingSpline numIterations styleFn points =
    mconcat $ zipWith render ticks (drop 2 ticks)
  where
    spline  = cubicSpline False points :: Located (Trail _ _)
    ticks   = [0, 1 / fromIntegral numIterations .. 1]
    render s e = stroke (section spline s e) # styleFn s

@ghost
Copy link
Author

ghost commented Mar 26, 2019

@bacchanalia
Thanks, gotta wrap my head around section.
For some reason this gives me weird results (looks cool tho):
https://imgur.com/abQZSpt (with drop 2 ticks)
https://imgur.com/p4pezKW (with tail ticks)

Edit:
I've found out setting lineCap LineCapSquare fixes the ripple, but it still looks wrong.
Opacity is incorrect because of the overlapping.
In this case, spline should already be half transparent in the middle:
https://imgur.com/46cpuRN

@bacchanalia
Copy link

lineCap LineCapSquare works for basically the same reason as dropping ticks works: the line cap is thick and therefore causes overlap. The pattern still shows up to a lesser extent with drop 2 ticks because the overlap isn't sufficient. You can increase the overlap but have finer control by increasing 2. Increasing the overlap with drop causes not enough opacity at the beginning, so I found changing the first arg to replicate n 0 ++ ticks is better. You still need to to turn down the opacity to compensate for the overlap.

morphingSpline :: _ => Int -> (Double -> _ -> _) -> [P2 Double] -> Diagram b
morphingSpline numIterations styleFn points =
    mconcat $ zipWith render (replicate 10 0 ++ ticks) ticks
  where
    spline = cubicSpline False points :: Located (Trail _ _)
    ticks  = [0, 1 / fromIntegral numIterations .. 1]
    render s e = stroke (section spline s e) # styleFn s
points = map p2 [(0,0), (200,300), (500,-200), (-400,100), (0,300)]
-- using opacity/5 for n = 10 as a guess
styleFn t = opacity ((1 - t)/5) . lw (local (500*(1 - t) / 3))
diagram = morphingSpline 1000 styleFn points # pad 1.3 # bg white

morphingSpline

@ghost
Copy link
Author

ghost commented Mar 26, 2019

@bacchanalia
Running your code gives me this. What backend do you use?
I'm using Rasterific-0.7.4.2, diagrams-rasterific-1.4.1.1, diagrams-lib-1.4.2.3, diagrams-core-1.4.1.1.

cvt_500x500_1139926420499568107

Edit:
Ah, I just noticed #322, it's probably not in any released version yet (I've used stack with lts-13.12).
Thank you, that looks great!

@bacchanalia
Copy link

Right, I should have mentioned that section was fixed in HEAD, but the fix hasn't been released yet. Sorry about that.

@ghost
Copy link
Author

ghost commented Mar 27, 2019

@bacchanalia
Could you please check if this works fine for you?

morphingSpline :: _ => Int -> (Double -> _ -> _) -> [P2 Double] -> Diagram b
morphingSpline numIterations styleFn points = mconcat $ zipWith render ticks (tail ticks) -- (replicate 10 0 ++ ticks) ticks
  where
    spline = cubicSpline False points :: Located (Trail _ _)
    ticks  = [0, 1 / fromIntegral numIterations .. 1]
    render s e = stroke (section spline s e) # styleFn s

points = map p2 [(0,0), (200,300), (500,-200), (-400,100), (0,300)]
styleFn t = id -- opacity ((1 - t)/5) . lw (local (500*(1 - t) / 3))
diagram = morphingSpline 1000 styleFn points # pad 1.3 # bg white

@bacchanalia
Copy link

@ivxvm
pentagramearrings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants