/
rotate.lua
117 lines (105 loc) · 4.4 KB
/
rotate.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
local pdf = require("justenoughlibtexpdf")
local enter = function (self)
if not self.rotate then return end
local x = -math.rad(self.rotate)
-- Keep center point the same
pdf:gsave()
local cx = self:left():tonumber()
local cy = -self:bottom():tonumber()
pdf.setmatrix(1, 0, 0, 1, cx + math.sin(x) * self:height():tonumber(), cy)
pdf.setmatrix(math.cos(x), math.sin(x), -math.sin(x), math.cos(x), 0, 0)
pdf.setmatrix(1, 0, 0, 1, -cx, -cy)
end
local leave = function(self)
if not self.rotate then return end
pdf:grestore()
end
if SILE.typesetter.frame then
enter(SILE.typesetter.frame)
table.insert(SILE.typesetter.frame.leaveHooks, leave)
end
table.insert(SILE.framePrototype.enterHooks, enter)
table.insert(SILE.framePrototype.leaveHooks, leave)
-- What is the width, depth and height of a rectangle width w and height h rotated by angle theta?
-- rect1 = Rectangle[{0, 0}, {w, h}]
-- {{xmin, xmax}, {ymin, ymax}} = Refine[RegionBounds[TransformedRegion[rect1,
-- RotationTransform[theta, {w/2,h/2}]]],
-- w > 0 && h > 0 && theta > 0 && theta < 2 Pi ]
-- PiecewiseExpand[xmax - xmin]
-- \[Piecewise] -w Cos[theta]-h Sin[theta] Sin[theta]<=0&&Cos[theta]<=0
-- w Cos[theta]-h Sin[theta] Sin[theta]<=0&&Cos[theta]>0
-- -w Cos[theta]+h Sin[theta] Sin[theta]>0&&Cos[theta]<=0
-- w Cos[theta]+h Sin[theta] True
local outputRotatedHbox = function (self, typesetter, line)
local origbox = self.value.orig
local x = self.value.theta
-- Find origin of untransformed hbox
local save = typesetter.frame.state.cursorX
typesetter.frame.state.cursorX = typesetter.frame.state.cursorX - (origbox.width.length-self.width)/2
local horigin = (typesetter.frame.state.cursorX + origbox.width.length / 2):tonumber()
local vorigin = -(typesetter.frame.state.cursorY + origbox.height / 2):tonumber()
SILE.outputters.libtexpdf._init()
pdf:gsave()
pdf.setmatrix(1, 0, 0, 1, horigin, vorigin)
pdf.setmatrix(math.cos(x), math.sin(x), -math.sin(x), math.cos(x), 0, 0)
pdf.setmatrix(1, 0, 0, 1, -horigin, -vorigin)
origbox:outputYourself(typesetter, line)
pdf:grestore()
typesetter.frame.state.cursorX = save
typesetter.frame:advanceWritingDirection(self.width)
end
SILE.registerCommand("rotate", function(options, content)
local angle = SU.required(options, "angle", "rotate command")
local theta = -math.rad(angle)
local origbox = SILE.call("hbox", {}, content)
SILE.typesetter.state.nodes[#SILE.typesetter.state.nodes] = nil
local h = origbox.height + origbox.depth
local w = origbox.width.length
local st = math.sin(theta)
local ct = math.cos(theta)
local height, width, depth
if st <= 0 and ct <= 0 then
width = -w * ct - h * st
height = 0.5*(h-h*ct-w*st)
depth = 0.5*(h+h*ct+w*st)
elseif st <=0 and ct > 0 then
width = w * ct - h * st
height = 0.5*(h+h*ct-w*st)
depth = 0.5*(h-h*ct+w*st)
elseif st > 0 and ct <= 0 then
width = -w * ct + h * st
height = 0.5*(h-h*ct+w*st)
depth = 0.5*(h+h*ct-w*st)
else
width = w * ct + h * st
height = 0.5*(h+h*ct+w*st)
depth = 0.5*(h-h*ct-w*st)
end
depth = -depth
if depth < 0 then depth = 0 end
SILE.typesetter:pushHbox({
value = { orig = origbox, theta = theta},
height = height,
width = width,
depth = depth,
outputYourself= outputRotatedHbox
})
end)
return { documentation = [[\begin{document}
The \code{rotate} package allows you to rotate things. You can rotate entire
frames, by adding the \code{rotate=<angle>} declaration to your frame declaration,
and you can rotate any content by issuing the command \code{\\rotate[angle=<angle>]\{...\}},
where \code{<angle>} is measured in degrees.
Content which is rotated is placed in a box and rotated. The height and width of
the rotated box is measured, and then put into the normal horizontal list for
typesetting. The effect of this is that space is reserved around the rotated content.
The best way to understand this is by example: here is some text rotated by
\rotate[angle=10]{ten}, \rotate[angle=20]{twenty} and \rotate[angle=40]{forty} degrees.
The previous line was produced by the following code:
\begin{verbatim}
\line
here is some text rotated by
\\rotate[angle=10]\{ten\}, \\rotate[angle=20]\{twenty\} and \\rotate[angle=40]\{forty\} degrees.
\line
\end{verbatim}
\end{document}]] }