1 module rpui.render.transforms;
2 
3 import std.math;
4 
5 import gapi.vec;
6 import gapi.font;
7 import gapi.text;
8 import gapi.transform;
9 import gapi.texture;
10 
11 import rpui.events;
12 import rpui.widget;
13 import rpui.render.components;
14 import rpui.alignment;
15 
16 interface TransformsSystem {
17     void onProgress(in ProgressEvent event);
18 }
19 
20 private vec2 toScreenPosition(in float windowHeight, in vec2 position, in float height) {
21     return vec2(floor(position.x), floor(windowHeight - height - position.y));
22 }
23 
24 QuadTransforms updateQuadTransforms(
25     in CameraView cameraView,
26     in vec2 position,
27     in vec2 size
28 ) {
29     QuadTransforms transforms;
30 
31     with (transforms) {
32         transform.position = toScreenPosition(cameraView.viewportHeight, position, size.y);
33         transform.scaling = size;
34         modelMatrix = create2DModelMatrix(transform);
35         mvpMatrix = cameraView.mvpMatrix * modelMatrix;
36     }
37 
38     return transforms;
39 }
40 
41 LinesTransforms updateLinesTransforms(in CameraView cameraView, in vec2 position) {
42     LinesTransforms transforms;
43 
44     with (transforms) {
45         const screenPosition = toScreenPosition(cameraView.viewportHeight, position, 0);
46         modelMatrix = create2DModelMatrixPosition(screenPosition);
47         mvpMatrix = cameraView.mvpMatrix * modelMatrix;
48     }
49 
50     return transforms;
51 }
52 
53 BlockTransforms updateBlockTransforms(
54     in float[ChainPart][BlockRow] partWidths,
55     in float[BlockRow] partHeights,
56     in CameraView cameraView,
57     in vec2 position,
58     in vec2 size,
59 ) {
60     BlockTransforms transforms;
61 
62     const topSize = vec2(size.x, partHeights[BlockRow.top]);
63     const bottomSize = vec2(size.x, partHeights[BlockRow.bottom]);
64     const middleSize = vec2(size.x, size.y - topSize.y - bottomSize.y);
65 
66     const topPosition = position;
67     const middlePosition = topPosition + vec2(0, topSize.y);
68     const bottomPosition = middlePosition + vec2(0, middleSize.y);
69 
70     transforms.topChain = updateHorizontalChainTransforms(
71         partWidths[BlockRow.top],
72         cameraView,
73         topPosition,
74         topSize
75     );
76 
77     transforms.middleChain = updateHorizontalChainTransforms(
78         partWidths[BlockRow.middle],
79         cameraView,
80         middlePosition,
81         middleSize
82     );
83 
84     transforms.bottomChain = updateHorizontalChainTransforms(
85         partWidths[BlockRow.bottom],
86         cameraView,
87         bottomPosition,
88         bottomSize
89     );
90 
91     return transforms;
92 }
93 
94 HorizontalChainTransforms updateHorizontalChainTransforms(
95     in float[ChainPart] partWidths,
96     in CameraView cameraView,
97     in vec2 position,
98     in vec2 size,
99     in Widget.PartDraws partDraws = Widget.partDraws.all
100 ) {
101     HorizontalChainTransforms transforms;
102 
103     auto leftSize = vec2(partWidths[ChainPart.left], size.y);
104     auto rightSize = vec2(partWidths[ChainPart.right], size.y);
105     auto centerSize = vec2(size.x - leftSize.x - rightSize.x, size.y);
106 
107     auto leftPos = position;
108     auto centerPos = leftPos + vec2(leftSize.x, 0);
109     auto rightPos = centerPos + vec2(centerSize.x, 0);
110 
111     switch (partDraws) {
112         case Widget.PartDraws.left:
113             centerSize.x += rightSize.x;
114             break;
115 
116         case Widget.PartDraws.center:
117             centerPos = position;
118             centerSize = size;
119             break;
120 
121         case Widget.PartDraws.right:
122             centerPos = leftPos;
123             centerSize.x += leftSize.x;
124             break;
125 
126         default:
127             // Nothing
128     }
129 
130     with (transforms) {
131         quadTransforms[ChainPart.left] = updateQuadTransforms(cameraView, leftPos, leftSize);
132         quadTransforms[ChainPart.center] = updateQuadTransforms(cameraView, centerPos, centerSize);
133         quadTransforms[ChainPart.right] = updateQuadTransforms(cameraView, rightPos, rightSize);
134     }
135 
136     return transforms;
137 }
138 
139 HorizontalChainTransforms updateVerticalChainTransforms(
140     in float[ChainPart] partWidths,
141     in CameraView cameraView,
142     in vec2 position,
143     in vec2 size
144 ) {
145     HorizontalChainTransforms transforms;
146 
147     const topSize = vec2(size.x, partWidths[ChainPart.top]);
148     const bottomSize = vec2(size.x, partWidths[ChainPart.bottom]);
149     const middleSize = vec2(size.x, size.y - topSize.y - bottomSize.y);
150 
151     const topPos = position;
152     const middlePos = topPos + vec2(0, topSize.y);
153     const bottomPos = middlePos + vec2(0, middleSize.y);
154 
155     with (transforms) {
156         quadTransforms[ChainPart.top] = updateQuadTransforms(cameraView, topPos, topSize);
157         quadTransforms[ChainPart.middle] = updateQuadTransforms(cameraView, middlePos, middleSize);
158         quadTransforms[ChainPart.bottom] = updateQuadTransforms(cameraView, bottomPos, bottomSize);
159     }
160 
161     return transforms;
162 }
163 
164 UiTextTransforms updateUiTextTransforms(
165     UiTextRender* uiText,
166     Font* font,
167     in UiTextTransforms oldTransforms,
168     in UiTextAttributes attrs,
169     in CameraView cameraView,
170     in vec2 position,
171     in vec2 size = vec2(0, 0)
172 ) {
173     UiTextTransforms measure;
174 
175     if (oldTransforms.cachedString != attrs.caption) {
176         UpdateTextInput updateTextInput = {
177             textSize: attrs.fontSize,
178             font: font,
179             text: attrs.caption
180         };
181 
182         const textUpdateResult = updateTextureText(&uiText.text, updateTextInput);
183 
184         uiText.texture = textUpdateResult.texture;
185         measure.size = textUpdateResult.surfaceSize;
186     } else {
187         measure.size = oldTransforms.size;
188     }
189 
190     measure.cachedString = attrs.caption;
191     vec2 textPosition = position + attrs.offset;
192 
193     if (size != vec2(0, 0)) {
194         textPosition.x += alignBox(attrs.textAlign, measure.size.x, size.x);
195         textPosition.y += verticalAlignBox(attrs.textVerticalAlign, measure.size.y, size.y);
196     }
197 
198     const Transform2D textTransform = {
199         position: toScreenPosition(cameraView.viewportHeight, textPosition, measure.size.y),
200         scaling: measure.size
201     };
202 
203     measure.mvpMatrix = cameraView.mvpMatrix * create2DModelMatrix(textTransform);
204     measure.relativePosition = textPosition - position;
205 
206     return measure;
207 }
208 
209 vec2 getUiTextBounds(
210     UiTextRender* uiText,
211     Font* font,
212     in UiTextAttributes attrs
213 ) {
214     UpdateTextInput updateTextInput = {
215         textSize: attrs.fontSize,
216         font: font,
217         text: attrs.caption
218     };
219 
220     return getTextBounds(&uiText.text, updateTextInput);
221 }