3
$\begingroup$

If I plot a sphere using Graphics3D with BoxRatios->1, then I get a nice looking plot:

Show[Graphics3D[{Red, Sphere[{0, 0, 0}, 1]}], BoxRatios -> 1, 
 PlotRange -> {{-4, 4}, {-4, 4}, {-4, 4}}]

Image:

well-scaled sphere

However, if PlotRange or BoxRatios changes, then the sphere gets distorted:

distorted examples

If I know the BoxRatios and PlotRange before plotting, I can use Ellipsoid to make something that appears spherical in any 3D plot:

ellipsoid that appears spherical

Question: Is there some way I can create my own custom "Graphics Object" that plots an ellipsoid in 3D that is scaled based on the dimensions of the plot (BoxRatios and PlotRange) to appear perfectly spherical? I would like my "Graphics Object" to automatically adjust to BoxRatios and PlotRange the same way that PointSize automatically adjusts to the width of the plot without need for user input.

Note: You might be curious why I am asking this question. The reason is that Graphics3D[{PointSize[0.035],Red,Point[{0,0,0}]}] produces a point in 3D that looks like a flat disk, and I would like to represent points in 3D as perfectly-scaled spheres. Sphere on its own is not a perfect answer because it can appear distorted based on the dimensions of the plot.

$\endgroup$

1 Answer 1

3
$\begingroup$

Update: While we wait for some future release updates to Sphere that allows Offset[o] and Scaled[s] to specify the radius (as is currently the case for Disk and Circle) , we can use post-processing as follows:

If the input is a Graphics object and the option values BoxRatios and PlotRange are not known in advance, we can extract them from the object before we use scaleSphere.

Multicolumn[
 glist = Show[Graphics3D[{Red, Sphere[{0, 0, 0}, 1]}], BoxRatios -> #,
      PlotRange -> #2, 
     PlotLabel -> Row[{"BoxRatios: ", #, "\n  PlotRange: ", #2}], 
     ImageSize -> Medium] & @@@ ratiorangepairs, 2]

enter image description here

ClearAll[postProcess]
postProcess = # /. s_Sphere :> 
     scaleSphere[BoxRatios /. Quiet@AbsoluteOptions[#, BoxRatios], 
       PlotRange@#][s] &;


Multicolumn[#, 2] &[postProcess /@ glist]

enter image description here

Original answer:

With known box ratios and plot ranges you can use Scale to construct a function to process Sphere[...]:

ClearAll[scaleSphere]

scaleSphere[br_, pr_] := Scale[#, Normalize[-Subtract @@@ pr, Max] / br] &;

Examples:

ratiorangepairs =  Tuples[{{1, {1, 2, 3}}, 
  {{{-4, 4}, {-4, 4}, {-4, 4}}, {{-4, 4}, {-4, 4}, {-1, 1}}}}]; 

Multicolumn[#, 2] &[Show[Graphics3D[{Red, scaleSphere[##]@Sphere[{0, 0, 0}, 1]}], 
    BoxRatios -> #, PlotRange -> #2, 
    PlotLabel -> Row[{"BoxRatios: ", #, "\n  PlotRange: ", #2}], 
    ImageSize -> Medium] & @@@ ratiorangepairs]

enter image description here

$\endgroup$
1
  • $\begingroup$ Thanks for sharing this! I apologize if I was unclear--I want to spherically-scaled ellipsoids without knowing BoxRatios or PlotRange ahead of time. This would be similar to Graphics3D[{PointSize[0.02],Point[{0,0,0}]}] knowing to scale the point to 2% of the width of the plot without knowing the dimensions of the plot ahead of time. $\endgroup$ Oct 18, 2020 at 18:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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