# How to define a custom 2/3D graphic primitive like built-in?

There are lots of basic graphic-primitives in Mathematica, such as 2D- primitive: Line[], Circle[], Disk[] and 3D-primitive: Cylinder[], BSplineSurface[], Sphere[], Cuboid[],etc.

In general, they own the following basic usage if memory serves:

Graphics(*Graphics3D*)[
{primitiveStyle1, primitive1[arg1],
primitiveStyle2, primitive2[arg2]}]

Graphics(*Graphics3D*)[
{{primitiveStyle1, primitive1[arg1],
primitiveStyle2, primitive2[arg2]}}]


where, primitiveStyle is one/several of the member of Thick, Green and so on.

For this question, the MSE has owned two similar threads like:

For the first question, Simon Woods has given a good answer for 2D-case. However, the detail about the implementation for the 2D primitive was not described. In addtion, m_goldberg also given a solution with the help of the built-in primitive.

For instance, now I have to create a 3D primitive BezierSurface[] via the built-in BezierFunction[], I tried using UpValues[] as follows:

BezierSurface /: Graphics3D[BezierSurface[ctrlnets_]] :=
ParametricPlot3D[
BezierFunction[ctrlnets][u, v], {u, 0, 1}, {v, 0, 1}]


Now we have a test

cpts = Table[{i, j, RandomReal[{-1, 1}]}, {i, 5}, {j, 5}];
Graphics3D[BezierSurface[cpts]]


But, for Graphics3D[{BezierSurface[cpts]}], it failed:(. I tried to change the UpValues[] defintion:

BezierSurface /: Graphics3D[{BezierSurface[ctrlnets_]}] :=
ParametricPlot3D[
BezierFunction[ctrlnets][u, v], {u, 0, 1}, {v, 0, 1}]


which gives me TagSetDelayed::tagpos: error.

For the BSplineSurface[], which own many usages

(*basic usage 1*)
Graphics3D[BSplineSurface[cpts]]

(*basic usage 2*)
Graphics3D[{BSplineSurface[cpts]}]

Graphics3D[{Pink, EdgeForm[{Thick, Blue}], Specularity[White, 50],
BSplineSurface[cpts]}]

Graphics3D[{PointSize[Medium], Blue, Map[Point, cpts],
Pink, BSplineSurface[cpts]}]

(*another usage*)
Graphics3D[{{Blue, Cylinder[]},
{Red, Sphere[{0, 0, 2}]}}]


So my question is:

• how to make user-defined 3D primitive BezierSurface[] works like built-in BSplineSurface[]?
• What you call primitiveStyle is often referred to as a directive. Commented Mar 30, 2016 at 1:55
• @J.M. Did you means Directive[g1,g2,...], represents a single graphics directive composed of the directives g1,g2,... ?
– xyz
Commented Mar 30, 2016 at 1:57
• Yes, that is the symbol used to treat a group of directives as a single directive. :) Commented Mar 30, 2016 at 2:00
• You could do something less fancy like, BezierSurface[ctrlnets_] := First@ParametricPlot3D[...]. Commented Mar 30, 2016 at 2:02
• @wxffles Yes, this is indeed a solution. However, BSplineSurface[cpts] just returns itself, while BezierSurface[cpts] returns a lot of 3D points group.
– xyz
Commented Mar 30, 2016 at 2:06

If my answer for the 2D case lacks detail, it's because TypesetMakeBoxes is an internal, undocumented function. That makes it hard to say anything authoritative about how it works. Essentially though, we are defining the custom primitive in such a way that the definition only applies during conversion of a graphics expression to boxes.

Here's a version of createPrimitive adapted for Graphics3D.

SetAttributes[createPrimitive3D, HoldAll]

createPrimitive3D[patt_, expr_] :=
TypesetMakeBoxes[p : patt, fmt_, Graphics3D] :=
With[{e = expr}, TypesetMakeBoxes[Interpretation[e, p], fmt, Graphics3D]]


You use it like SetDelayed, with a pattern as the first argument and the definition as the second:

createPrimitive3D[BezierSurface[ctrlnets_],
FirstCase[
ParametricPlot3D[BezierFunction[ctrlnets][u, v], {u, 0, 1}, {v, 0, 1}],
_GraphicsComplex]]


BezierSurface does not evaluate...

cpts = Table[{i, j, RandomReal[{-1, 1}]}, {i, 3}, {j, 3}];
BezierSurface[cpts]

(* BezierSurface[{{{1, 1, -0.559961}, {1, 2, -0.470345}, {1, 3, 0.271683}},
{{2, 1, 0.855812}, {2, 2, -0.378208}, {2, 3, 0.476702}},
{{3, 1, -0.106064}, {3, 2, -0.462438}, {3, 3, 0.269039}}}] *)


...except when displayed in Graphics3D

Graphics3D[BezierSurface[cpts]]


• +1! Thanks very very much:) I tested the following cases:(1) Graphics3D[BezierSurface[cpts]], (2) Graphics3D[{BezierSurface[cpts]}], (3) Graphics3D[{BezierSurface[cpts], BSplineSurface[cpts]}], (4) Graphics3D[{Opacity[0.4], BezierSurface[cpts]}], (5) Graphics3D[{Specularity[White, 10], Red, BezierSurface[cpts]}]. And I discovered that the last case(5) failed.
– xyz
Commented Jul 30, 2016 at 14:53
• In addtion, I also noticed that the built-in BSplineSurface[] didn't apply the ParametricPlot3D[] to sample the 3D points, please see here :)
– xyz
Commented Jul 30, 2016 at 14:59
• The GraphicsComplex extracted from ParametricPlot3D contains its own colouring directives which is why your attempt to recolour it doesn't work. To get full control of the styling you probably want to extract only the polygons. Commented Jul 31, 2016 at 13:03
• BSplineSurface does not need to construct a polygon representation, it displays as a BSplineSurface3DBox which is drawn directly by the front end. Commented Jul 31, 2016 at 13:10
• I find that I have to supply the 3rd and 4th argument to FirstCase` in order for this to work in V12. Commented May 27, 2020 at 18:04