我找到了John Fultz 的一篇文章,其中非常清楚地解释了图形功能的工作原理:
在版本 6 中,内核完全不参与生成渲染图像。在版本 6 中显示图形所采取的步骤与显示非图形输出所采用的步骤非常相似。它的工作原理如下:
1) 表达式被求值,最终产生带有 headGraphics[]
或的东西Graphics3D[]
。
2) 结果表达式通过MakeBoxes
. MakeBoxes
有一套规则将图形表达式转换为前端用来表示图形的框语言。例如,
In[9]:= MakeBoxes[Graphics[{Point[{0, 0}]}], StandardForm]
Out[9]= GraphicsBox[{PointBox[{0, 0}]}]
在内部,我们称之为“排版”表达式。将图形视为“排版”可能有点奇怪,但它基本上与排版的操作相同(这种方式已经工作了 11 年),所以我将使用这个术语。
3) 生成的排版表达式通过 MathLink 发送到前端。
4)前端解析排版表达式并创建内部对象,这些对象通常与排版表达式一一对应。
5)前端渲染内部对象。
这意味着转换是在内核中通过调用MakeBoxes
.
这个调用可以通过高级代码拦截:
list = {};
MakeBoxes[expr_, form_] /; (AppendTo[list, HoldComplete[expr]];
True) := Null;
HoldComplete[Rotate[Style[expr, Red], 0.5]]
ClearAll[MakeBoxes];
list
这是我们得到的输出:
一看就知道MakeBoxes
不尊重HoldAllComplete
属性。
在发送到前端之前自动转换的符号列表可以从FormatValues
:
In[1]:= list =
Select[Names["*"],
ToExpression[#, InputForm,
Function[symbol, Length[FormatValues@symbol] > 0, HoldAll]] &];
list // Length
During evaluation of In[1]:= General::readp: Symbol I is read-protected. >>
Out[2]= 162