Slow 3D rotation with antialiasing in Mathematica 7

After Mr. Wizard pointed out that changing the ViewAngle improved performance, I had a hunch as to what was causing it.

Normally, when you rotate graphics in Mathematica, you'll notice the box that contains the graphics resizes itself as the image is rotated around. Why? Most of the settings are set to the default of Automatic, so they try to fiddle around to make the graphics fit nicely within the screen. Specifically, Plot3D (and many other 3D Plot functions) have a named parameter called RotationAction. According to the Mathematica documentation:

3D graphics by default resize and move to fit when they are interactively rotated

This parameter defaults to RotationAction->"Fit", which tries to always make sure the entire 3D graphics fits on screen. Instead, you can force the graphics to clip using RotationAction->"Clip". This causes the bounding box to remain the same. Instead of resizing, it merely chops off whatever goes off screen. Performance is improved dramatically as Mathematica doesn't have to continuously reshape the image to make everything fit on screen.

Example code:

Plot3D[{x^2 + y^2, -(x^2 + y^2)}, {x, -2, +2}, {y, -2, +2}, 
 ImageSize -> 700, RotationAction -> "Fit"]

Plot3D[{x^2 + y^2, -(x^2 + y^2)}, {x, -2, +2}, {y, -2, +2}, 
 ImageSize -> 700, RotationAction -> "Clip"]

Some further notes

Upon further analysis, RotationAction->"Clip" has some issues with rotation initially. On the very first rotation of the graphics, it rotates extremely fast. I don't know why this is so, only Wolfram Research could possibly answer that. After the initial rotation though, it rotates at the normal speed as any other graphics though.

The smoothest way of accomplishing nice rotation for me was given by:

 Plot3D[..., RotationAction -> "Clip", ViewAngle -> 0.4]

This is as close to the default clipping volume as possible, without any strange issues with fast rotations or cut off graphics.


Based on acl's idea:

EventHandler[
 Plot3D[{x^2 + y^2, -x^2 - y^2}, {x, -2, 2}, {y, -2, 2}, 
  ImageSize -> 700, RotationAction -> "Clip", 
  SphericalRegion -> 
   True], {"MouseUp" :> (SetOptions[$FrontEndSession, 
     RenderingOptions -> {"HardwareAntialiasingQuality" -> 1.}]), 
  "MouseClicked" :> (SetOptions[$FrontEndSession, 
     RenderingOptions -> {"HardwareAntialiasingQuality" -> 1.}]), 
  "MouseDown" :> (SetOptions[$FrontEndSession, 
     RenderingOptions -> {"HardwareAntialiasingQuality" -> 0.}])}, 
 PassEventsDown -> True]

You can see what happens with the option with:

Dynamic["HardwareAntialiasingQuality" /. 
  Last@Last@Options[$FrontEnd, RenderingOptions], 
 UpdateInterval -> .1]

How it works: it switches off antialiasing for the currend FrontEnd session when you rotate the plot and switches it on when you release the mouse. The only problem is that when the mouse is released and antialiasing is switched on, the FrontEnd does not render the graphics again to make it antialiased. The solution is to click the plot without rotating it - and it becomes antialiased!


Building off of Alexey's answer, I changed the code slightly so it doesn't modify the front end settings:

aa = True;
EventHandler[
 Style[
  Plot3D[{x^2 + y^2, -(x^2 + y^2)}, {x, -2, +2}, {y, -2, +2}, 
   ImageSize -> 700, RotationAction -> "Clip", 
   SphericalRegion -> True],
   Antialiasing -> Dynamic[aa, TrackedSymbols :> {aa}]],
 {"MouseUp" :> (aa = True), "MouseDown" :> (aa = False)}, 
 PassEventsDown -> True]

It accomplishes the same, but it avoids modifying $FrontEnd.

Here is a function which automatically tacks on the necessary logic for providing dynamically changing antialiasing:

Clear[DynamicAA]; (* Perhaps I should also add the HoldFirst attribute too, not sure *)
DynamicAA[graphics_] := Module[{aa},
  aa = True;
  EventHandler[
   Style[graphics, 
    Antialiasing -> Dynamic[aa, TrackedSymbols :> {aa}]], {"MouseUp" :> (aa = True), 
    "MouseDown" :> (aa = False)}, PassEventsDown -> True]]

Usage is what you what expect:

DynamicAA[Plot3D[{x^2 + y^2, -x^2 -y^2}, {x, -2, +2}, {y, -2, +2}, ImageSize->700]]

Perhaps someone else can think of something clever with Mouseover. This works by displaying a different expression when the mouse is over the displayed object or not; try this to see:

Mouseover[
 Style[
  Graphics[Circle[]],
  Antialiasing \[Rule] True
  ],
 Style[
  Graphics[Circle[]],
  Antialiasing \[Rule] False
  ]
 ]

Unfortunately I have no idea how one would switch antialiasing on and off, nor do I know how to make something like Plot[f,range,Option->Mouseover[1,2]].

The problem seems to be that Mouseover doesn't evaluate to different things when the cursor is/is not over it, but rather it displays different things--or so it looks.

Maybe someone who understands dynamic constructs better can work out how to use the infrastructure that lets Mouseover do its thing to solve your problem.