1
$\begingroup$

My goal here is to visualize 3D data using an oblique cabinet projection. I would like to be able to adjust the "depth" or spacing of the Z-axis, maintain a golden ratio between the X and Y axes, and avoid cropping any data out of the figure.

So far I've found that I can use Graphics3D's ViewMatrix option to rescale the data to a unit cube, then project that cube as desired. This approach gives me the desired projection, but pushes most of my z-axis out of the PlotRange, cropping the data. PlotRange doesn't seem to adjust the plot to include the projected values.

Example: no depth-scaling excludes data from plot

I can apply a rescaling transform to the cube to warp the Z-axis depth, but then I get a gap between the top of the figure (where the title is) and the plot. This seems to be because the Graphics3D object is forced to remain square shaped, which doesn't apply when the ViewMatrix option goes unspecified.

Example: scaling z-axis introduces whitespace

Is there some way to include all the data within the figure and maintain the desired aspect ratio? The data shown are just a toy example, so I'd like this to be a generalizable approach if possible. I've been struggling with this for a while so any tips would be greatly appreciated. Thank you!!

Code for synthetic example:

data = Quiet@
       Table[Table[{z, x, 
          Exp[-((x - 3 - z)/(.2*(1 + z/4)))^2] + .75 (1 + 
              z/10) Exp[-((x - 1 - z/2)/(.5 (1 - z/15)))^2]}, {x, 0, 10, 
          0.02}], {z, 0, 8, 1}];
    
(* rescale to unit cube and warp it *)
zDepth = 0.1;
rescaleToUnitCube = RescalingTransform[{{0, 8}, {0, 10}, {0, 1.35}}];
scaleDepth = ScalingTransform[{zDepth, 1, 1/GoldenRatio}];
transformationMatrix = 
  TransformationMatrix@Composition[scaleDepth, rescaleToUnitCube];

(* oblique projection  *)
projectionMatrix = {{1/Sqrt[2], 1, 0, 0}, {1/Sqrt[2], 0, 1, 0}, {1, 0,
     0, 0}, {0, 0, 0, 1}};

(* plotting results *)
ListPointPlot3D[
 data,
 PlotLabel -> "Example Title",
 ViewMatrix -> {transformationMatrix, projectionMatrix},
 PlotRange -> {All, All, All}]
$\endgroup$
0
$\begingroup$

Use the option PlotRegion to specify "what region of the final display area a plot should fill" and add the plot label using the function Labeled:

Labeled[ListPointPlot3D[data, 
  ViewMatrix -> {transformationMatrix, projectionMatrix}, 
  PlotRange -> {All, All, All},
  PlotRegion -> {{.1, .9}, {.1, 1.3}}],
 Style["Example Title", "GraphicsLabel"], Top]

enter image description here

| improve this answer | |
$\endgroup$
  • $\begingroup$ This is helpful - thank you. Is there no way to achieve the same while maintaining the Graphics3D head? $\endgroup$ – sbernasek Nov 9 at 21:21
  • $\begingroup$ @sbernasek, you can use Show[ListPointPlot3D[data, ViewMatrix -> {transformationMatrix, projectionMatrix}, PlotRange -> {All, All, All}, PlotRegion -> {{.1, .9}, {.1, 1.2}}], Graphics3D[Text[Style["Example Title", "GraphicsLabel"], Scaled@{.5, .5, 1.15}]]]? $\endgroup$ – kglr Nov 9 at 21:30
  • $\begingroup$ Perfect - thanks again. $\endgroup$ – sbernasek Nov 9 at 21:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.