Isometric perspective for Graphics3D
[Edit notice: Updated to allow the setting of the vertical direction of the plot and to fix an error.]
Here is a slight generalization of my answer to Isometric 3d Plot. To get an isometric view, we need to construct a ViewMatrix
that will rotate a vector of the form {±1, ±1, ±1}
to {0, 0, 1}
and project orthogonally onto the first two coordinates.
ClearAll[isometricView];
isometricView[
g_Graphics3D, (* needed only for PlotRange *)
v_ /; Equal @@ Abs[N@v] && 1. + v[[1]] != 1., (* view point {±1, ±1, ±1} *)
vert_: {0, 0, 1}] := (* like ViewVertical; default: z-axis *)
{TransformationMatrix[
RescalingTransform[
EuclideanDistance @@
Transpose[Charting`get3DPlotRange@ g] {{-1/2, 1/2}, {-1/2, 1/2}, {-1/2, 1/2}}].
RotationTransform[{-v, {0, 0, 1}}].
RotationTransform[{vert - Projection[vert, v], {0, 0, 1} - Projection[{0, 0, 1}, v]}].
RotationTransform[Mod[ArcTan @@ Most[v], Pi], v].
TranslationTransform[-Mean /@ (Charting`get3DPlotRange@ g)]],
{{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
foo = Graphics3D[Cuboid[{-.5, -.5, -.5}, {1., 2., 4}]];
Show[foo, ViewMatrix -> isometricView[foo, {1, 1, 1}, {0, 0, 1}],
ImagePadding -> 20, Axes -> True, AxesLabel -> {x, y, z}]
Show[foo, ViewMatrix -> isometricView[foo, {-1, 1, 1}, {1, 1, 0}],
ImagePadding -> 20, Axes -> True, AxesLabel -> {x, y, z}]
All combinations of viewpoints and vertical axes:
Notes:
Getting an accurate plot range that includes the padding is important to computing the correct view matrix. There are alternatives to the undocumented internal function Charting`get3DPlotRange
.
Alexey Popkov has a method here:
How to get the real PlotRange using AbsoluteOptions?
I used PlotRange /. AbsolutOptions[g, PlotRange]
and multiplied by 1.02
(I don't recall why not something like 1.04
) to approximate the padding in my answer to Isometric 3d Plot.
My go-to resource for understanding ViewMatrix
has been especially Heike's answer to Extract values for ViewMatrix from a Graphics3D.
This update is in response to Yves' comment. Working with the axes made me realize that the coordinate system is flipped (from "right-handed" to "left-handed). Hence I changed the projection from IdentityMatrix[4]
to one that flips the x & y coordinates.
It might be a good idea to Deploy
the graphics to prevent rotation by the mouse. When the graphics are rotated, the front end resets the ViewMatrix
in a rather ugly way.
You can use the following post-process function to apply a general parallel projection:
parallelProjection[g_Graphics3D, axes_, pad_: 0.15] :=
Module[{pr3, pr2, ar, t},
pr3 = {-pad, pad} (#2 - #) & @@@ # + # &@Charting`get3DPlotRange@g;
pr2 = MinMax /@ Transpose[[email protected]];
ar = Divide @@ Subtract @@@ pr2;
t = AffineTransform@Append[Transpose@axes, {0, 0, -1}];
t = RescalingTransform@Append[pr2, pr3[[3]]].t;
Show[g, AspectRatio -> 1/ar,
ViewMatrix -> {TransformationMatrix[t], IdentityMatrix[4]}]];
Here axes
defines the projection of x,y,z axes to 2d plane and pad
makes a room to display axes labels.
Isometric projection:
g = Graphics3D[Cuboid[], Axes -> True, AxesLabel -> {X, Y, Z}];
parallelProjection[g, {{-Sqrt[3]/2, -1/2}, {Sqrt[3]/2, -1/2}, {0, 1}}]
Cabinet projection:
α = π/4;
parallelProjection[g, {{1, 0}, {0, 1}, -{Cos[α]/2, Sin[α]/2}}]
As of V11.2 we can use a combination of ViewProjection
and ViewPoint
:
Graphics3D[Cuboid[], ViewProjection -> "Orthographic", ViewPoint -> {1, 1, 1}]
Various vantages:
v = Tuples[{Tuples[{-1, 1}, 3], IdentityMatrix[3]}];
Graphics3D[Cuboid[{-.5, -.5, -.5}, {1., 2., 4}], ViewProjection -> "Orthographic",
ViewPoint -> #1, ViewVertical -> #2] & @@@ v