# How is a 3D plot drawn in 2D from an arbitrary viewpoint?

I am working on a project that involves SciDraw. The SciDraw software is great for making high quality plots, but it is not compatible with any 3D plots.

I need to add some relatively simple 3D plots -- just a few points with lines connecting them (e.g. to draw a tetrahedron). I have started researching how to do this using a projection and found valuable resources here and here. This may not even turn out to be useful for the project at hand, but I've become curious how Mathematica plots 3D points (objects, curves, etc.) into 2D (i.e. my computer screen) while maintaining the 3D view.

My attempts to replicate this so far have been only partially successful. I have been testing my function by trying to reproduce the 3D rendering of a cube:

Graphics3D[Cube[], ViewPoint -> #] & /@ {{1, 0, 0}, {1, 1, 0}, {1, 1, 1}}


My first attempt was as follows:

dropDimension[{x_, y_, z_}, {a_, b_, c_}] :=
Block[{orthogonalVecs = {{-b, a, 0}, {-c, 0, a}, {0, -c, b}}, s, t,
u, v, equation},
{u, v} =
Select[DeleteDuplicates[orthogonalVecs],
EuclideanDistance[#, {0, 0, 0}] != 0 &][[;; 2]];
equation =
MaximalBy[{{1, u[[1]]*v[[2]] - v[[1]]*u[[2]]}, {2,
u[[1]]*v[[3]] - v[[1]]*u[[3]]}, {3,
u[[2]]*v[[3]] - v[[2]]*u[[3]]}}, #[[2]]^2 &][[1]];
Switch[equation[[1]],
1, {s = ((x - a)*v[[2]] - (y - b)*v[[1]])/equation[[2]],
t = ((y - b)*u[[1]] - (x - a)*u[[2]])/equation[[2]]},
2, {s = ((x - a)*v[[3]] - (z - c)*v[[1]])/equation[[2]],
t = ((z - c)*u[[1]] - (x - a)*u[[3]])/equation[[2]]},
3, {s = ((y - b)*v[[3]] - (x - a)*v[[2]])/equation[[2]],
t = ((z - c)*u[[2]] - (y - b)*u[[3]])/equation[[2]]}
]
]


I then generated a random sampling of points on the surface of a cube using:

cubePts =
Table[ReplacePart[Table[RandomReal[{-1, 1}], 3],
RandomChoice[Range[3]] -> RandomChoice[{-1, 1}]], 10000];


Finally, I plotted all the points:

Table[ListPlot[dropDimension[#, viewPoint] & /@ cubePts,
PlotRange -> All], {viewPoint, {{1, 0, 0}, {1, 1, 0}, {1, 1, 1}}}]


My main issue with my current solution is that you can't see the depth of the original object (the cube surface). For example, from {1,1,0}, I would expect the center (s=1) to be taller than the surrounding areas. My naïve attempt to introduce this depth involved calculating the depth of each point as the distance perpendicular to the viewing plane (i.e. in the axis of the view point) between a given point and the view point. However, I am still uncertain of the correct depth function, though I am currently using depth = ({a, b, c} - {x, y, z}) . {a, b, c}.

Any insight as how to Mathematica renders 3D plots into 2D, or how I might be able to mimic this behavior in a relatively simple function? Or, if there is a built-in function to do something similar, I would love to know -- my searches through the documentation haven't produced anything yet.

## 1 Answer

Without thinking about camera position or ViewProjections or anything complicated, you literally just omit the z coordinate of each point like so

pts = MeshPrimitives[Import[
"https://upload.wikimedia.org/wikipedia/commons/9/93/Utah_teapot_(solid).stl"
,"STL"],0][[;; , 1]];
ListPlot@pts[[;;,{1,2}]] (* up view *)
ListPlot@pts[[;;,{2,3}]] (* front view *)
ListPlot@pts[[;;,{1,3}]] (* left view *)


Just do a rotation transform before projecting, and voila

Manipulate[ListPlot[
(pts.RotationMatrix[a,{0,0,1}].RotationMatrix[b,{1,0,0}])[[;;,{1,2}]],
PlotRange->{{-10,10},{-10,10}}],{a,0,2\[Pi]},{b,0,2\[Pi]}]


• I realize I haven't actually answered your question as you're specifically interested in camera viewpoints, I suppose. I'm going to try some things, but in the mean time perhaps Heike's answer here mathematica.stackexchange.com/questions/3528/… is helpful.
– Adam
Commented Sep 8, 2022 at 23:44