Transformer-DSL 0.0.3

Transformer-DSL 0.0.3

测试已测试
语言语言 Obj-CObjective C
许可证 BSD 2.0
发布最后发布2014年12月

未声明维护。



  • Jonathan Wight

这是什么?

这是一个针对 CoreAnimation 变换(CATransform3D)的特定领域语言。

不再是像这样编写代码

CATransform3D *theTransform = CATransform3DScale(CATransform3DMakeTranslation(1, 2, 0), 10, 10, 1);

而是这样编写代码

#import "NIMTransformFormatter.h"

CATransform3D theTransform = CATransform3DMakeWithFormat(@"translate(1,2) | scale(10,10,1.0)");

甚至更短

CATransform3D theTransform = CATransform3DMakeWithFormat(@"t(1,2) | s(10,10,1.0)");

如果您有直接使用类的原因。

CATransform3D theTransform = [[NIMTransformFormatter formatterWithFormat:@"t(1,2) | s(10,10,1.0)"] CATransform3D];

最后,如果您要转换现有的 CATransform3D,可以使用这种形式(感谢 @ntakayama 的建议。)

someLayer.transform = CATransform3DWithFormat(someLayer.transform, @"t(1,2) | s(10,10,1.0)");

语法看起来是什么样子?

目前它在 Backus-Naur 形式下看起来是这样的(理论上)

<operation-name>  ::= "translate" | "scale" | "rotate" | "identity"
<placeholder>     ::= "%f"
<parameter>       ::= <floating-point-number> | <placeholder>
<parameter-list>  ::= <parameter> | <parameter-list> "," <parameter>
<operation>       ::= <operation-name> "(" <parameter-list> ")"
<concat-operator> ::= "|"
<operation-list>  ::= <operation> | <concat-operator> <operation-list>
(this is pretty incorrect BNF - dont rely on it yet)

要注意的关键部分是操作看起来有点像函数,并且使用 |(竖线)字符连接。

请注意,操作名称不区分大小写,并且可以缩短到仅第一个唯一的字符(例如,“t”对应于“translate”)。

格式化示例

这返回仅矩阵单位阵。您可能永远不会需要使用它

identity()

这返回一个按 X 和 Y 缩放 10.0,Z 缩放 1.0 的单位阵。

identity() | scale(10, 10, 1)

默认情况下隐含单位阵。因此,前面的格式等效于此格式。注意,如果传递自己的基础变换,则操作将相对于该变换。

scale(10, 10, 1)

我们可以推断 Z 缩放。缩放操作中的任何参数都推断为 1.0

scale(10, 10)

这是一个平移操作,沿 X 轴向下 100 像素

translate(-100, 0, 0)

但当然,我们可以推断其他两个轴

translate(-100)

让我们把这些组合起来

scale(10, 10) | translate(-100)

让我们再缩短一下

s(10,10) | t(-100)

参数怎么办?

显然您不会在每次变换时传递常数。因此,变换字符串首先经过 sprintf 格式化。这允许您编写类似以下代码

CATransform3D *theTransform = CATransform3DMakeWithFormat(@"translate(%f,%f) | scale(10,10,1.0)", X, Y);

但是为什么?

使用多个嵌套函数创建复杂变换很痛苦。最终你会得到代码,难以快速更改,例如,如果需要重新排序变换操作或在某一选项之间添加另一个操作。

使用这种特定领域语言创建变换非常简单。操作不嵌套,因此您可以很容易地从左到右读取操作。您可以很容易地重新排序操作并在操作列表中插入操作。

这还可以用来做什么?

这个函数的一个用途可能是如果你想将变换保存到数据中(比如你的plist文件或JSON文件)。以字符串形式表示变换会非常方便。要最大化其可用性,我们还需要支持将矩阵转换为字符串。请参阅“进一步的想法”部分。

性能?

这个领域特定语言(DSL)比手动执行操作要慢得多。

2014-03-20 18:20:21.168 TransformTest[59794:303] Time for non-parametized expression: 0.15035
2014-03-20 18:20:21.571 TransformTest[59794:303] Time for parametized expression: 0.400879
2014-03-20 18:20:21.574 TransformTest[59794:303] Time for raw CA function calls: 0.00237203
2014-03-20 18:20:21.574 TransformTest[59794:303] Slow-down factor for non-parametrized: 63.3846
2014-03-20 18:20:21.575 TransformTest[59794:303] Slow-down factor for parametrized: 169.003
Program ended with exit code: 0

这意味着使用不带参数的格式字符串大约比使用CA函数执行变换慢60倍。使用带参数的格式字符串则大约慢170倍。性能差异的原因是非参数化的表达式很容易被缓存 - 格式只解析一次。

这太慢了吗?这取决于你如何使用这些变换。如果你只是使用这些变换来计算动画的初始值和最终值,可能没问题。如果你自己进行动画并以此每秒60帧计算变换,可能就太慢了。

许可证

BSD 2条款,见LICENSE文件。

进一步的想法

  • 更多的优化。通过降低到C语言级别来提高整体性能。
  • 命名参数:translate(z = -100)
  • 在旋转中使用度符号°。
  • 编写代码以支持将任意矩阵转换为字符串。有关更多信息,请参阅从旋转矩阵计算欧拉角。或者支持matrix3D()操作。
  • 实现一个"asSourceCode"方法,它返回变换的源代码 - 要么是一系列嵌套函数,要么是带参数预计算的最终变换。