19
$\begingroup$

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]]

enter image description here

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.

enter image description here

For the BSplineSurface[], which own many usages

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

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

(*adding style*)
Graphics3D[{Pink, EdgeForm[{Thick, Blue}], Specularity[White, 50], 
            BSplineSurface[cpts]}]

(*adding other primitives*)
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[]?
$\endgroup$
6
  • 1
    $\begingroup$ What you call primitiveStyle is often referred to as a directive. $\endgroup$ Mar 30, 2016 at 1:55
  • $\begingroup$ @J.M. Did you means Directive[g1,g2,...], represents a single graphics directive composed of the directives g1,g2,... ? $\endgroup$
    – xyz
    Mar 30, 2016 at 1:57
  • $\begingroup$ Yes, that is the symbol used to treat a group of directives as a single directive. :) $\endgroup$ Mar 30, 2016 at 2:00
  • $\begingroup$ You could do something less fancy like, BezierSurface[ctrlnets_] := First@ParametricPlot3D[...]. $\endgroup$
    – wxffles
    Mar 30, 2016 at 2:02
  • 1
    $\begingroup$ @wxffles Yes, this is indeed a solution. However, BSplineSurface[cpts] just returns itself, while BezierSurface[cpts] returns a lot of 3D points group. $\endgroup$
    – xyz
    Mar 30, 2016 at 2:06

1 Answer 1

19
+500
$\begingroup$

If my answer for the 2D case lacks detail, it's because Typeset`MakeBoxes 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_] :=
 Typeset`MakeBoxes[p : patt, fmt_, Graphics3D] :=
  With[{e = expr}, Typeset`MakeBoxes[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]]

enter image description here

$\endgroup$
8
  • $\begingroup$ +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. $\endgroup$
    – xyz
    Jul 30, 2016 at 14:53
  • $\begingroup$ In addtion, I also noticed that the built-in BSplineSurface[] didn't apply the ParametricPlot3D[] to sample the 3D points, please see here :) $\endgroup$
    – xyz
    Jul 30, 2016 at 14:59
  • $\begingroup$ 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. $\endgroup$ Jul 31, 2016 at 13:03
  • 1
    $\begingroup$ BSplineSurface does not need to construct a polygon representation, it displays as a BSplineSurface3DBox which is drawn directly by the front end. $\endgroup$ Jul 31, 2016 at 13:10
  • 1
    $\begingroup$ I find that I have to supply the 3rd and 4th argument to FirstCase in order for this to work in V12. $\endgroup$
    – Jason B.
    May 27, 2020 at 18:04

Your Answer

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

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