Tim Shedelbower, Wolfram Research

(RowDefault[, (Spacer1), , (Spacer1), ])  #WolframTechConf

Abstract

One of many wonderful components waiting to be discovered in Wolfram Language is powerful graphics. I will share some of my techniques developed to harness this power for documentation illustrations and marketing images. Learn style, lighting and camera tips to solve mysteries you may have encountered while creating your visualizations. Leave encouraged to explore the broad range of graphic functions.

The Five Tricks

Style your data quickly.

Highlight interesting parts of your data.

Build hybrid visualizations for deeper insight of your data.

Construct simple lighting to accent your data.

Set the best view to emphasize your data.

Style

Style your data quickly.

PlotTheme

Type “PlotTheme->” then choose from the auto completion list.

Plot[{Sin[(x^2)],Sinc[x^2]},{x,0,2Pi},PlotTheme->"Business"]

Theme combinations

Combine a base theme with feature themes and add options.

Plot[{Sin[(x^2)],Sinc[x^2]},{x,0,2Pi},PlotStyle->112,PlotTheme->{"Business","Wide"}]

TIP - Theme option settings

Discover existing theme option settings:

Charting`ResolvePlotTheme["Business",Plot]

Highlight

Highlight interesting parts of your data.

Surface features

Highlight part of a surface with ColorFunction option.

Set ColorFunctionScaling to False for blend values greater than 1.

Increase PlotPoints for smoother blending.

Plot3D[5Sin[((((x^2)+(y^2)))^(2/3))]/(x+y),{x,0,8},{y,0,8},ColorFunction->(Blend[{{0,Gray},{1,Red},{4,Yellow}},#3]&),ColorFunctionScaling->False,Mesh->False,PlotPoints->60,PlotRange->All]

Specific area

Highlight a specific area by combining graphics with Show function.

Style highlight object with Black and Glow[color] for unshaded color.

Show[
Plot3D[Sin[((((x^2)+(y^2)))^(2/3))]/(x+y),{x,0,8},{y,0,8},PlotRange->All],
Graphics3D[{EdgeForm[White],Opacity[.2],Black,Glow[Hue[.6,1,1]],Cuboid[{2,2,-.3},{8,8,.3}]}]
]

Style highlight object with AmbientLight[color] for shaded color.

Show[
Plot3D[Sin[((((x^2)+(y^2)))^(2/3))]/(x+y),{x,0,8},{y,0,8},PlotRange->All],
Graphics3D[{EdgeForm[White],Opacity[.5],AmbientLight[Hue[.6,1,1]],Cuboid[{2,2,-.3},{8,8,.3}]}]
]

Shape projection

Project a shape by plotting an extra surface.

Style extra surface with Black and Glow[color] for unshaded color.

Plot3D[{ -3 Sin[( x^2 + (y-1) ^2)]+x+2 y,-15},{x,-2,2},{y,-2,2},BoundaryStyle->Gray,BoxRatios->{1,1,.4},
MeshFunctions->(#3&),PlotStyle->{Automatic,Directive[Black,Glow[LightGray]]},RegionFunction->Function[{x,y},Norm[{x,y}]≥1&&y^4+x^4 ≤ 10]]

TIP - Glow[color]

Glow controls shading color.

Use Black with Glow to ignore lighting.

Labeled[Graphics3D[{#,Sphere[]},Boxed->False,ImageSize->100],#]&/@{White,Pink,Glow[Pink],Directive[Black,Glow[Pink]]}

Hybrid

Build hybrid visualizations for deeper insight of your data.

Combine 2D or 3D with Show function

Show function options override those in the graphics expressions.
Otherwise the first occurrence of an option is used which might be a default value.

Lighting is one of the those default values. Use inline lighting to override it.

data3d=GaussianMatrix[{20,10},{4,5}];
data2d=ListContourPlot[data3d];
Show[
ListPlot3D[data3d,Mesh->30,MeshFunctions->(#3&)],
Graphics3D[{AmbientLight[White],DirectionalLight[Black,ImageScaled[{1,1,1}]],
GraphicsComplex[Append[#,Min[data3d]]&/@data2d[[1,1]],data2d[[1,2]]]}],
BoxRatios->{1,1,1},ViewPoint->{1,-3,1.4}
]

Combine 2D with Epilog or Prolog options

Prolog renders in back. Epilog renders in front.

Use only the graphics directives (first part) of additional visualization.

data=Table[Sin[x],{x,0,2Pi,Pi/10}];

ListLinePlot[data,Prolog->BarChart[data,BarSpacing->0][[1]],PlotStyle->Thickness[.01]]

Lighting

Construct simple lighting to accent your data.

Simple lighting

AmbientLight controls shading darkness.

DirectionalLight controls shading direction.

Specularity controls highlight.

Use gray lights to avoid the color blending with object color.

ImageScaled light position locks it to the camera position.

Set both PlotStyle and Lighting to override default settings.

ListPlot3D[GaussianMatrix[{20,10},{5,5}],Mesh->False,
PlotStyle->Directive[Specularity[Gray,50],ColorData[106,1]],Lighting->{AmbientLight[GrayLevel[.2]],DirectionalLight[White,ImageScaled[{1,1,1}]]}]

Use a list of directives in PlotStyle to color multiple plots.

ListPlot3D[{GaussianMatrix[{20,10},{5,5}],GaussianMatrix[{20,8},{5,5}]},Mesh->False,
PlotStyle->(Directive[Specularity[Gray,50],#]&/@ColorData[106,"ColorList"]),Lighting->{AmbientLight[GrayLevel[.2]],DirectionalLight[White,ImageScaled[{1,1,1}]]}]

Color Shading

AmbientLight controls shading color

ListPlot3D[GaussianMatrix[{20,10},{5,5}],Mesh->False,
PlotStyle->Directive[Specularity[Gray,50],ColorData[106,1]],Lighting->{AmbientLight[Darker[ColorData[106,1],.2]],DirectionalLight[White,ImageScaled[{1,1,1}]]}]

Move AmbientLight to PlotStyle for multiple colors

ListPlot3D[{GaussianMatrix[{20,10},{5,5}],GaussianMatrix[{20,8},{5,5}]},Mesh->False,
PlotStyle->(Directive[Specularity[Gray,50],AmbientLight[Darker[#,.3]],#]&/@ColorData[106,"ColorList"]),Lighting->{DirectionalLight[White,ImageScaled[{1,1,1}]]}]

TIP - Lighting presets

Automatic uses colored lights.

"Accent" uses one gray light. Created for ToonShading, GoochShading, HatchShading, HalftoneShading and StippleShading.

"Neutral" has a large highlighted area. Created for ColorFunction.

"ThreePoint" has a key light, fill light and back light. Created for MaterialShading.

Grid[{Labeled[Graphics3D[Sphere[],Boxed->False,Lighting->#[[1]],ImageSize->100],Column[{Style[#[[1]],ShowStringCharacters->True],Labeled[#[[2]],Style["top view",15]]},Alignment->Center]]&/@{{Automatic,Magnify[,3]},{"Accent",Magnify[,3]},{"Neutral",Magnify[,3]},{"ThreePoint",Magnify[,3]}}},Spacings->3]

TIP - ImageScaled coordinates

ImageScaled coordinates lock the light to the camera view.

Style[Grid[{
Insert[Flatten[Table[Labeled[Plot3D[Sin[x]Sin[y],{x,-4,5},{y,-4,5},ImageSize->200,Lighting->{AmbientLight[GrayLevel[.2]],DirectionalLight[If[loc===ImageScaled[{1,1,1}],GrayLevel[1],GrayLevel[.6]],loc]},Method->{"ShrinkWrap"->True},Mesh->False,
PlotStyle->Directive[ColorData[106,1]],PlotTheme->{"SolidGrid","Minimal"},ViewPoint->vp],loc],{loc,{ImageScaled[{1,1,1}],3{1,1,1}}},{vp,{{2,-2.4,2},{-1.5,-2.4,2}}}]],Spacer[50],3]
}],"Text",18]

View

Set the best view to emphasize your data.

Perspective

Multiply ViewPoint to control perspective without changing the view.

Large multipliers generate an orthographic view.

ListPlot3D[GaussianMatrix[{20,10},{5,5}],ViewPoint->.6{2,-1,1}]

Interactive position

rotate (drag)          zoom (cmd-drag)          pan (shift-drag)

ListPlot3D[GaussianMatrix[{20,10},{5,5}]]

Collect view options

Options[,Names["View*"|"ImageSize"]]

TIP - Explore camera functions

Dynamic demonstration of the six camera functions.

cameraPreview[]

More to explore

guide/GraphicsDirectives

guide/3DGraphicsOptions

Init

FrustumCorners

Clear[Tools`FrustumCorners]
Tools`FrustumCorners[vp_,vd_,vr:{near_,far_},vv_:{0,0,1},va_:35Degree,vc_:{0,0}]:=
Block[{vx,vy,u,v,nbase,nsize,ncorners,fbase,fsize,fcorners,res},
vy=vv;
vx=Normalize[Cross[vd,vy]];
vy=Cross[vx,vd];

(* Calculate corners of near plane *)
nbase=vp+vd*near;
nsize=ConstantArray[near*Tan[va],2];
ncorners=Join@@Table[nbase+nsize[[1]]*(x-2vc[[1]])*vx+nsize[[2]]*(y-2vc[[2]])*vy,{x,-1,1,2},{y,-1,1,2}];
ncorners=ncorners[[{1,2,4,3}]];

(* Calculate corners of far plane *)
fbase=vp+vd*far;
fsize=ConstantArray[far*Tan[va],2];
fcorners=Join@@Table[fbase+fsize[[1]]*x*vx+fsize[[2]]*y*vy,{x,-1,1,2},{y,-1,1,2}];
fcorners=fcorners[[{1,2,4,3}]];

res=Join[ncorners,fcorners];
res/;ArrayQ[res]
]

Tools`FrustumCorners[___]:=$Failed

FrustumLines

Clear[Tools`FrustumLines]
Tools`FrustumLines[expr___]:=
Block[{corners,res},
corners=Tools`FrustumCorners[expr];
(
res=GraphicsComplex[corners,
Line/@{
{1,5},
{2,6},
{3,7},
{4,8}
}
]
)/;ArrayQ[corners]
]

Tools`FrustumLines[___]:=$Failed

cameraPreview

cameraPreview[]:=DynamicModule[{fstyle={"Notes",12,Black},axisoffset={0,0,.01},gridoffset={0,0,0},ledpt,rot=0,arrowLength=2.5,minmax=3,windowpos=.55,viewPts,frustumPoints,frustumPointsOffset,grid,gridInfinite,axes,ctrview,ctrviewoffset,colHlt=ColorData[106,3],colVA,colVC,colVP,colVR,colVVec,colVVer,colBot,showVA,showVC,showVP,showVR,showVVec,showVVer,copy,viewRot=.4,viewVert=3,gridsize=2,
opts={Boxed->False,Lighting->{AmbientLight[GrayLevel[.3]],DirectionalLight[White,ImageScaled[{.5,1,4}]]}},
obj={Hue[.25,1,1,.3],EdgeForm[GrayLevel[0,.7]],Rotate[,Pi/2,{0,0,1}]}
},
showVA=showVP=True;
showVC=showVR=showVVec=showVVer=False;
axes={
{Gray,AbsoluteThickness[1],Arrowheads[Medium],Arrow[axisoffset+#&/@{{0,0,0},#}]&/@{{arrowLength,0,0},{0,arrowLength,0},{0,0,arrowLength}}},
{Text[Style["x",fstyle,"TI"],{arrowLength+.1,0,0},.75{1,1}],Text[Style["y",fstyle,"TI"],{0,arrowLength+.1,0},{-1,0}],Text[Style["z",fstyle,"TI"],{0,0,arrowLength},{0,-1.5}]}
};
grid={
{Opacity[.7],Black,Glow[GrayLevel[.8]],EdgeForm[],Polygon[gridoffset+#&/@{{-gridsize,-gridsize,0},{gridsize,-gridsize,0},{gridsize,gridsize,0},{-gridsize,gridsize,0}}]},
{White,Table[{Line[gridoffset+#&/@{{-gridsize,a,0},{gridsize,a,0}}],Line[gridoffset+#&/@{{b,-gridsize,0},{b,gridsize,0}}]},{a,-gridsize,gridsize,.5},{b,-gridsize,gridsize,.5}]}
};
gridInfinite={
{Opacity[.7],Black,Glow[GrayLevel[.8]],EdgeForm[],InfinitePlane[gridoffset+#&/@{{-gridsize,-gridsize,0},{gridsize,-gridsize,0},{gridsize,gridsize,0}}]},
{White,AbsoluteThickness[1],Table[{Line[gridoffset+#&/@{{-2gridsize,a,0},{2gridsize,a,0}}],Line[gridoffset+#&/@{{b,-2gridsize,0},{b,2gridsize,0}}]},{a,-2gridsize,2gridsize,.5},{b,-2gridsize,2gridsize,.5}]}
};
Manipulate[
viewPts={2cy-1,2cx-1,0}+va*#&/@({{-1,-1,0},{1,-1,0},{1,1,0},{-1,1,0}});
frustumPoints=Tools`FrustumCorners[{x,y,z},Normalize[{vtx,vty,vtz}-{x,y,z}],{near,far},Normalize[{vx,vy,vz}],va/2];
frustumPointsOffset=Tools`FrustumCorners[{x,y,z},Normalize[{vtx,vty,vtz}-{x,y,z}],{near,far},Normalize[{vx,vy,vz}],va/2,{cx,cy}-{.5,.5}];
ctrview=Mean[frustumPoints[[1;;4]]];
ctrviewoffset=Mean[frustumPointsOffset[[1;;4]]];
path[u_]:=({x,y,z}+u({vtx,vty,vtz}-{x,y,z})+{vx,vy,vz}.001Sin[u Pi+$MachineEpsilon]);
frenet[u_]=FrenetSerretSystem[path[u], u][[2]];
transform[u_]:= Composition[TranslationTransform[path[u]], FindGeometricTransform[frenet[u],{{0, 0, 1}, {1, 0, 0}, {0, 1, 0}} ][[2]]];
{colVA,colVC,colVP,colVR,colVVec,colVVer,colBot}=
{GrayLevel[0,1],Hue[.3,1,.5],ColorData[106,3],Directive[Opacity[.7],Black,Glow[Hue[.125,.5,1]]],ColorData[106,3],ColorData[106,1],Gray};
Style[Grid[{{
Graphics3D[{
obj,
If[showVA,{Black,Glow[Hue[.1,.5,.9]],Opacity[.1],EdgeForm[],Table[Polygon[{{x,y,z},Sequence@@p}],{p,frustumPoints[[#]]&/@{{1,2},{2,3},{3,4},{4,1}}}]},{}],
If[showVA,{colVA,Opacity[1],AbsoluteDashing[{6,3}],Table[Line[{{x,y,z},p}],{p,frustumPoints[[1;;4]]}]},{}],
If[showVP,{colVP,Sphere[{x,y,z},.1]},{}],
If[showVVec,{colVVec,AbsoluteThickness[3],Arrowheads[.06],Arrow[Tube[{{x,y,z},{vtx,vty,vtz}},.05]]},{}],
If[showVVer,{colVVer,AbsoluteThickness[3],Arrowheads[.06],Arrow[Tube[{{0,0,0},2{vx,vy,vz}},.05]]},{}],
If[showVVer,{Black,Opacity[.5],Glow[Hue[.1,.5,.8]],EdgeForm[],InfinitePlane[{{x,y,z},ctrview,ctrview-{vx,vy,vz}}]},{}],
If[showVC,{AbsoluteThickness[1],Arrowheads[.03],colVC,Arrow[Tube[{ctrviewoffset+.45(frustumPoints[[3]]-frustumPoints[[4]]),ctrviewoffset-.45(frustumPoints[[3]]-frustumPoints[[4]])}]],
Arrow[Tube[{ctrviewoffset+.45(frustumPoints[[4]]-frustumPoints[[1]]),ctrviewoffset-.45(frustumPoints[[4]]-frustumPoints[[1]])}]]
},{}],

{
{Line[frustumPoints[[{1,2,3,4,1}]]]},
{colVR,EdgeForm[GrayLevel[0,1]],Polygon[frustumPointsOffset[[1;;4]]]},
{Black,Glow[colBot],CapForm["Butt"],Tube[frustumPointsOffset[[{1,4}]],.06]}
},
If[showVR,{
{Black,Line[frustumPoints[[{5,6,7,8,5}]]]},
{colVR,EdgeForm[Black],Polygon[frustumPoints[[5;;8]]]},
{Black,Tools`FrustumLines[{x,y,z},Normalize[{vtx,vty,vtz}-{x,y,z}],{near,far},Normalize[{vx,vy,vz}],va/2]}
},{}],
axes,grid,
If[showVP||showVVec||showVA,Text[Style["camera",fstyle],{x,y,z},{0,-1.5}],{}]
},opts,ImageSize->300,Method->{"ShrinkWrap"->True},ViewPoint->{5Sin[viewRot Pi],5Cos[viewRot Pi],viewVert},ViewVertical->{0,0,1}],
Grid[{{Graphics[Inset[Graphics3D[{
obj,axes,gridInfinite
},(*Background->If[Head[colVR]===Directive,Opacity[.5,(List@@colVR)[[-1,1]]],colVR],*)opts,ImageSize->150,Method->{"EdgeDepthOffset"->False},ViewAngle->va,ViewCenter->{{vtx,vty,vtz},{cx,cy}},ViewPoint->{x,y,z},ViewRange->{near,far},ViewVector->{{x,y,z},{vtx,vty,vtz}},ViewVertical->{vx,vy,vz}]],ContentSelectable->False,ImageSize->150]}},Spacings->{0,0},Dividers->{None,{None,Directive[Thickness[4],colBot]}}]
},
{"SCENE","CAMERA VIEW"},
{Spacer[{0,30}],Null},
{Grid[{{
Graphics[{Text[Style["θ",fstyle,colVA],{.83,.5},{-1,0}],colVA,AbsoluteThickness[1],Polygon[{{.63,.52},{.7Sin[.38Pi],.5+Cos[.38Pi]},{.76,.57}}],Line[Table[{.7Sin[x],.5+Cos[x]},{x,.38Pi,.62Pi,Pi/50}]],AbsoluteDashing[{6.2,3}],Line[{{1,1},{0,.5},{1,0}}]},AspectRatio->2/3,ImageSize->36],
Graphics[{
{If[Head[colVR]===Directive,Opacity[.5,(List@@colVR)[[-1,1]]],colVR],EdgeForm[Gray],Polygon[{{-1,-1},{1,-1},{1,1},{-1,1}}]},
{colVC,AbsoluteThickness[1],Arrowheads[.3],Arrow[{{.1,1},{.1,-1}}],Arrow[{{1,.1},{-1,.1}}]},
{colBot,AbsoluteThickness[3],CapForm["Butt"],Line[{{-1,-1},{1,-1}}]}},AspectRatio->1,ImageSize->30],
Graphics3D[{colVP,Sphere[{x,y,z},.1]},Boxed->False,ImageSize->10,Lighting->{AmbientLight[GrayLevel[.3]],DirectionalLight[White,ImageScaled[{.5,1,4}]]}],
Row[{Style["min",fstyle,"TI"],Graphics3D[{colVR,Polygon[2/3{{-1,-1,-1},{-1,1,-1},{-1,1,1},{-1,-1,1}}],colVR,Polygon[{{1,-1,-1},{1,1,-1},{1,1,1},{1,-1,1}}],GrayLevel[0,1],Line[{2/3{-1,-1,-1},{1,-1,-1}}],Line[{2/3{-1,1,-1},{1,1,-1}}],Line[{2/3{-1,1,1},{1,1,1}}],Line[{2/3{-1,-1,1},{1,-1,1}}]},AspectRatio->1,Boxed->False,ImageSize->{30,30},Method->{"ShrinkWrap"->True},ViewPoint->{1.12,-2.85,1.44}],Style["max",fstyle,"TI"]}],Graphics[{colVVec,AbsoluteThickness[3],Arrowheads[Medium],Arrow[{{0,0},{1,0}}]},AspectRatio->2/5,ImageSize->{50,20}],
Graphics[{colVVer,AbsoluteThickness[3],Arrowheads[Medium],Arrow[{{0,0},{1,0}}]},AspectRatio->2/5,ImageSize->{50,20}]},
{"ViewAngle","ViewCenter","ViewPoint","ViewRange","ViewVector","ViewVertical"}},Spacings->{1,Automatic}],SpanFromLeft},
{Grid[{{Checkbox[Dynamic@showVA],Checkbox[Dynamic@showVC],Checkbox[Dynamic@showVP],Checkbox[Dynamic@showVR],Checkbox[Dynamic@showVVec],Checkbox[Dynamic@showVVer]}},Spacings->4.5],SpanFromLeft}
}/.({Null,Null})->Sequence[]],fstyle],
"ViewAngle",{{va,30°},5°,45°,1°},Delimiter,"ViewCenter",{{cx,.5},-.5,1.5},{{cy,.5},-.5,1.5},Delimiter,"ViewPoint",{{x,1.5},-minmax,minmax},{{y,-2.5},-minmax,minmax},{{z,3},-minmax,minmax},Delimiter,"ViewRange",{{near,3,"min"},1,4},{{far,5.6,"max"},Dynamic[near],6},Delimiter,"ViewVector",{{vtx,0},-minmax,minmax},{{vty,0},-minmax,minmax},{{vtz,0},-minmax,minmax},Delimiter,"ViewVertical",{{vx,0},-1,1},{{vy,0},-1,1},{{vz,1},-1,1},TrackedSymbols:>{x,y,z,cx,cy,va,vx,vy,vz,vtx,vty,vtz,near,far,showVA,showVC,showVP,showVR,showVVec,showVVer}]
]