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

Projections - non-affine transformations #148

Merged
merged 13 commits into from
Feb 11, 2014
Merged

Projections - non-affine transformations #148

merged 13 commits into from
Feb 11, 2014

Conversation

bergey
Copy link
Member

@bergey bergey commented Jan 24, 2014

This is a proposal for non-affine transformations, which I've provisionally called Projections. It depends on diagrams/diagrams-core#50

Before this is ready to merge, we should choose a better name for applying a Projection. The obvious choice project conflicts with Data.VectorSpace.project. A couple of thoughts; I hope someone has better ideas:

  • warp
  • distort
  • stretch

Should this new type represent only projective transforms, or something more general? Either is fine with me, but I'd like to make it more clear in the docs. I originally set out to provide projective transforms, per #108. The current implementation is more general. The Projection type supports any spatial map, though small wiggles will probably be missed in the segment subdivision / approximation.

The answer to that might help inform the function name, and also whether it's semantically sensible to use Data.LinearMap inside Projection. I used LinearMap in an earlier version, and I'm happy to put it back in.

The use-cases I have in mind are illustrations of perspective, and quick-and-dirty 3D wireframes by transforming Paths in 3D, transforming to 2D, and using the 2D Backends. The latter requires 3D Paths and #117

@bergey
Copy link
Member Author

bergey commented Jan 24, 2014

Here are some examples, followed by the code. They show:

  • parallel projection onto a line
  • perspective projection onto a line
  • composition of Projection with Transformation (rotations)
  • 2 illustrations of the non-linear part of a perspective view, without the parallel projection

import Diagrams.Prelude
import Diagrams.TwoD.Project
import Diagrams.Project
import Diagrams.Backend.SVG

ptMark :: Diagram SVG R2
ptMark = circle 0.02 # lw 0

illustrateSegment :: FixedSegment R2 -> Diagram SVG R2
illustrateSegment (FLinear from to) = position [
    (from, ptMark # fc blue),
    (to,   ptMark # fc blue)]
illustrateSegment (FCubic from c1 c2 to) = position [
    (c1, ptMark # fc red),
    (c2, ptMark # fc red)] <> illustrateSegment (FLinear from to)

illustratePath t =
    stroke t
    <> (mconcat . map illustrateSegment . concatMap fixTrail . pathTrails $ t)

sq :: Path R2
sq = unitSquare # translate (5 ^& 3)

shaft = arc 0 (1/8 @@ turn)

curvedArrow = arrowBetween' (with & arrowShaft .~ shaft
                                  & shaftStyle %~ lc gray
                                  & headStyle  %~ fc gray
                            )

dashedLine :: P2 -> P2 -> Diagram SVG R2
dashedLine from to = fromVertices [from, to] # dashing [0.1, 0.1] 0

illustrateProjection :: Projection R2 -> Path R2 -> ([P2], [P2], Diagram SVG R2)
illustrateProjection t s = (basePts, projectedPts, projectedMarks) where
    basePts        = concat $ pathVertices sq
    projectedPts   = map (project t) basePts
    projectedMarks = position $ zip projectedPts (repeat (ptMark # fc green))

x0 = mconcat $ [illustratePath sq, projectedMarks]
               ++ zipWith curvedArrow basePts projectedPts
               ++ zipWith dashedLine basePts projectedPts
  where
    (basePts, projectedPts, projectedMarks) = illustrateProjection parallelX0 sq

x1 = mconcat $ [illustratePath sq, projectedMarks]
               ++ zipWith curvedArrow basePts projectedPts
               ++ zipWith dashedLine basePts (repeat origin)
  where
    (basePts, projectedPts, projectedMarks) = illustrateProjection perspectiveX1 sq

y0Rotated = mconcat $ [illustratePath sq, projectedMarks]
               ++ zipWith curvedArrow basePts projectedPts
  where
    parallelY0' = asProjection (rotation (-1/4 @@ turn)) <> parallelX0 <> asProjection (rotation (1/4 @@ turn))
    (basePts, projectedPts, projectedMarks) = illustrateProjection parallelY0' sq

skewSq = mconcat $  [stroke s, stroke (t s) # lc green]
                    ++ zipWith dashedLine facing final
                    ++ zipWith dashedLine (repeat origin) final where
  s = unitSquare # translate (1 ^& 0)
  t = project' 0.000001 facingX
  basePts = concat . pathVertices $ s
  facing = map (project facingX) basePts
  final  = map (project perspectiveX1) basePts

skewCircle = mconcat [stroke c, stroke (project' 0.01 facingX c) # lc green] where
  c = unitCircle # translate (2 ^& 0)

d = [x0, x1, y0Rotated, skewSq, skewCircle]

main = do
    renderSVG "projections.svg" (Dims 500 (300*(realToFrac $ length d))) .
         vcat' (with & sep .~ 0.5) $ d

@byorgey
Copy link
Member

byorgey commented Feb 8, 2014

I think representing more general things is great. Since we have to do approximations anyway, it seems there's nothing particularly to be gained by restricting to actual projective transforms (though I could be wrong).

warp and distort sound too much like they are referring to some specific transformation (like some weird effect you could apply in Photoshop or something). stretch just sounds like a synonym for scale. My counter-suggestions:

  • metamorphose
  • deform
  • transmogrify

@bergey
Copy link
Member Author

bergey commented Feb 8, 2014

I think including more general things is good. The main downside is that it's not clear (to me) what deformations are actually feasible. Because we sample at a few points and approximate, we'll stop the subdivision too soon on functions with small wiggles. But I'm fine crossing that bridge when we come to it, and we can probably use denser or more clever sampling if necessary in the future.

In that case, we should probably rename Projection and Projectable.

  • deform, Deformation, and Deformable are straightforward.
  • transmogrify, Transmogrification, and Transmogrifiable
  • metamorphose, Metamorphosis,Metamorphizes`?

I'm leaning towards the deform series as having the most obvious interpretation.

@byorgey
Copy link
Member

byorgey commented Feb 8, 2014

What, you don't like Metamorphable? ;-) I think the deform series sounds best to me too.

@jeffreyrosenbluth
Copy link
Member

what about simply morph

On Sat, Feb 8, 2014 at 4:13 PM, Brent Yorgey notifications@github.comwrote:

What, you don't like Metamorphable? ;-) I think the deform series sounds
best to me too.

Reply to this email directly or view it on GitHubhttps://github.com//pull/148#issuecomment-34556410
.

@byorgey
Copy link
Member

byorgey commented Feb 8, 2014

What would be the noun form? i.e. (transform, Transformation, Transformable) is to (morph, ?, Morphable)?

@bergey
Copy link
Member Author

bergey commented Feb 8, 2014

@jeffreyrosenbluth Haskell programmers will never forgive me if I define Morphism :: Point v -> Point v :)

@bergey
Copy link
Member Author

bergey commented Feb 8, 2014

I think this is ready to merge now, along with diagrams/diagrams-core#50

@byorgey
Copy link
Member

byorgey commented Feb 9, 2014

Looks good to me. If possible I'd like to have a patch to the user manual explaining deformations get merged at the same time.

@bergey
Copy link
Member Author

bergey commented Feb 9, 2014

Sounds good. I'll try to write something in the next couple of days.

byorgey added a commit that referenced this pull request Feb 11, 2014
Projections - non-affine transformations
@byorgey byorgey merged commit dad2704 into master Feb 11, 2014
@byorgey byorgey deleted the projections-rebase branch February 11, 2014 18:30
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

Successfully merging this pull request may close these issues.

3 participants