Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ShapeAreaTool.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(
3 "Shape Area Tool",
4 "Turns polylines and splines into specific shapes"
5 + "\nor creates a shape in the middle of the screen"
6 + "\n"
7 + "\nTo make a \"diamond\" (rhombus),"
8 + "\nuse Circle and set the number of points to 4"
9 + "\n"
10 + "\nPoints are created clockwise.",
11 awesomeFontCode: 0xF5EE)]
12class SCR_ShapeAreaTool : WorldEditorTool
13{
14 /*
15 Category: Global
16 */
17
21 [Attribute(
22 defvalue: "0",
23 desc: "Shape type to be created",
24 uiwidget: UIWidgets.ComboBox,
25 enums: SCR_ParamEnumArray.FromString("Circle,Can be used to create polygons;Rectangle;Star"),
26 category: "Global")]
27 protected int m_iShapeType;
28
29 [Attribute(defvalue: "48", desc: "Wanted shape's width", params: "0.001 inf", category: "Global")]
30 protected float m_fWidth;
31
32 [Attribute(defvalue: "32", desc: "Wanted shape's length", params: "0.001 inf", category: "Global")]
33 protected float m_fLength;
34
35 /*
36 Category: Circle
37 */
38
39 [Attribute(defvalue: "12", desc: "a minimum of 12 points is recommended for a round circle", params: "3 inf", category: "Circle")]
40 protected int m_iCircleSegmentsCount;
41
42 /*
43 Category: Rectangle
44 */
45
46 [Attribute(defvalue: "1", desc: "Nomber of segments per side", params: "1 inf", category: "Rectangle")]
47 protected int m_iRectangleSegmentsPerSide;
48
49 /*
50 Category: Star
51 */
52
53 [Attribute(defvalue: "5", desc: "Number of branches in the star", params: "2 inf", category: "Star")]
54 protected int m_iStarBranchesCount;
55
56 [Attribute(defvalue: "0.5", desc: "Star inner radius ratio - e.g 0.75 * width-length radius", uiwidget: UIWidgets.Slider, params: "0.01 1", category: "Star")]
57 protected float m_fStarInnerRadiusRatio;
58
59 /*
60 Category: Points
61 */
62
63 [Attribute(defvalue: "1", desc: "Snap shape points to terrain", category: "Points")]
64 protected bool m_bSnapToTerrain;
65
66 [Attribute(defvalue: "1", desc: "Whether or not points should be around the shape entity's origin or if the points should be in positive position only (shape origin being bottom-left)", category: "Points")]
67 protected bool m_bCentreOnPosition;
68
69 [Attribute(defvalue: "0", desc: "Define whether to close, open or leave as is the selected shape(s)", uiwidget: UIWidgets.ComboBox, enums: SCR_ParamEnumArray.FromString("Leave as is;Open;Close"), category: "Points")]
70 protected int m_iShapeClosing;
71
72 //------------------------------------------------------------------------------------------------
73 [ButtonAttribute("Create Polyline")]
74 protected void CreatePolyline()
75 {
76 CreateShape(false);
77 }
78
79 //------------------------------------------------------------------------------------------------
80 [ButtonAttribute("Create Spline")]
81 protected void CreateSpline()
82 {
83 CreateShape(true);
84 }
85
86 //------------------------------------------------------------------------------------------------
87 [ButtonAttribute("Convert Sel. Shapes")]
88 protected void ConvertSelectedShapes()
89 {
90 array<IEntitySource> shapeEntitySources = GetSelectedShapeEntitySources();
91 if (shapeEntitySources.IsEmpty())
92 {
93 Print("[SCR_ShapeAreaTool] No shapes were selected, no conversion needed", LogLevel.NORMAL);
94 return;
95 }
96
97 m_API.BeginEntityAction();
98
99 ConvertShapes(shapeEntitySources);
100
101 m_API.EndEntityAction();
102 }
103
104 //------------------------------------------------------------------------------------------------
107 protected void CreateShape(bool isSpline)
108 {
109 vector worldStart, worldPos, worldNormal;
110 if (!m_API.TraceWorldPos(m_API.GetScreenWidth() * 0.5, m_API.GetScreenHeight() * 0.5, TraceFlags.WORLD, worldStart, worldPos, worldNormal))
111 return;
112
113 m_API.BeginEntityAction();
114
115 IEntitySource shapeEntitySource;
116 if (isSpline)
117 shapeEntitySource = m_API.CreateEntity(((typename)SplineShapeEntity).ToString(), string.Empty, m_API.GetCurrentEntityLayerId(), null, worldPos, vector.Zero);
118 else
119 shapeEntitySource = m_API.CreateEntity(((typename)PolylineShapeEntity).ToString(), string.Empty, m_API.GetCurrentEntityLayerId(), null, worldPos, vector.Zero);
120
121 m_API.AddToEntitySelection(shapeEntitySource);
122
123 ConvertShapes({ shapeEntitySource });
124
125 m_API.EndEntityAction();
126 }
127
128 //------------------------------------------------------------------------------------------------
129 protected array<IEntitySource> GetSelectedShapeEntitySources()
130 {
131 array<IEntitySource> result = SCR_WorldEditorToolHelper.GetSelectedWorldEntitySources();
132
133 // remove all non-shapes
134 for (int i = result.Count() - 1; i >= 0; --i)
135 {
136 if (!m_API.SourceToEntity(result[i]).IsInherited(ShapeEntity))
137 result.Remove(i);
138 }
139
140 return result;
141 }
142
143 //------------------------------------------------------------------------------------------------
146 protected void ConvertShapes(notnull array<IEntitySource> shapeEntitySources)
147 {
148 if (shapeEntitySources.IsEmpty())
149 return;
150
151 array<vector> points = GetShapePoints();
152
153 foreach (IEntitySource shapeEntitySource : shapeEntitySources)
154 {
155 bool isSelected = m_API.IsEntitySelected(shapeEntitySource);
156 if (isSelected)
157 m_API.RemoveFromEntitySelection(shapeEntitySource); // prevents a Vector Tool point selection issue
158
159 SetShapePoints(shapeEntitySource, points);
160
161 if (m_iShapeClosing == 1) // open - default shape state
162 m_API.ClearVariableValue(shapeEntitySource, null, "IsClosed");
163 else
164 if (m_iShapeClosing == 2) // close
165 m_API.SetVariableValue(shapeEntitySource, null, "IsClosed", "1");
166// else // if (m_iShapeClosing == 0) // as is
167// leave as is
168
169 if (isSelected)
170 m_API.AddToEntitySelection(shapeEntitySource);
171 }
172 }
173
174 //------------------------------------------------------------------------------------------------
175 protected array<vector> GetShapePoints()
176 {
177 array<vector> result;
178 if (m_iShapeType == 0)
179 result = GetCirclePoints();
180 else
181 if (m_iShapeType == 2)
182 result = GetStarPoints();
183 else // default
184 result = GetRectanglePoints();
185
186 if (result.IsEmpty())
187 {
188 Print("Cannot modify shape(s), points calculation went wrong", LogLevel.ERROR);
189 return null;
190 }
191
192 if (!m_bCentreOnPosition)
193 {
194 vector offset = { m_fWidth * 0.5, 0, m_fLength * 0.5 };
195 foreach (int i, vector point : result)
196 {
197 result[i] = point + offset;
198 }
199 }
200
201 return result;
202 }
203
204 //------------------------------------------------------------------------------------------------
205 protected array<vector> GetCirclePoints()
206 {
207 array<vector> result = {};
208 float angleSize = Math.PI2 / m_iCircleSegmentsCount;
209
210 for (float i = Math.PI_HALF + Math.PI2, min = Math.PI_HALF; i > min; i -= angleSize) // clockwise
211 {
212 if (float.AlmostEqual(i, min)) // avoids a sometimes "closed" shape
213 break;
214
215 result.Insert({ Math.Cos(i) * m_fWidth * 0.5, 0, Math.Sin(i) * m_fLength * 0.5 });
216 }
217
218 return result;
219 }
220
221 //------------------------------------------------------------------------------------------------
222 protected array<vector> GetRectanglePoints()
223 {
224 if (m_iRectangleSegmentsPerSide)
225 return {
226 { m_fWidth * 0.5, 0, m_fLength * 0.5 }, // top-right
227 { m_fWidth * 0.5, 0, -m_fLength * 0.5 }, // bottom-right
228 { -m_fWidth * 0.5, 0, -m_fLength * 0.5 }, // bottom-left
229 { -m_fWidth * 0.5, 0, m_fLength * 0.5 }, // top-left
230 };
231
232 // multiple segments per side, go
233
234 array<vector> data = {
235 { m_fWidth * 0.5, 0, m_fLength * 0.5 }, // top-right
236 { 0, 0, -m_fLength / m_iRectangleSegmentsPerSide }, // offset
237
238 { m_fWidth * 0.5, 0, -m_fLength * 0.5 }, // bottom-right
239 { -m_fWidth / m_iRectangleSegmentsPerSide, 0, 0 }, // offset
240
241 { -m_fWidth * 0.5, 0, -m_fLength * 0.5 }, // bottom-left
242 { 0, 0, m_fLength / m_iRectangleSegmentsPerSide }, // offset
243
244 { -m_fWidth * 0.5, 0, m_fLength * 0.5 }, // top-left
245 { m_fWidth / m_iRectangleSegmentsPerSide, 0, 0 }, // offset
246 };
247
248 array<vector> result = {};
249 for (int i, count = data.Count(); i < count; i += 2) // step 2
250 {
251 vector startPoint = data[i];
252 vector offset = data[i + 1];
253
254 for (int j = 0; j < m_iRectangleSegmentsPerSide; ++j)
255 {
256 result.Insert(startPoint + (float)j * offset);
257 }
258 }
259
260 return result;
261 }
262
263 //------------------------------------------------------------------------------------------------
264 protected array<vector> GetStarPoints() // it is an oval of 4 points after all
265 {
266 array<vector> result = {};
267 float angleSize = Math.PI2 / m_iStarBranchesCount;
268
269 for (float i = Math.PI_HALF + Math.PI2, min = Math.PI_HALF; i > min; i -= angleSize) // clockwise
270 {
271 if (float.AlmostEqual(i, min)) // avoids a sometimes closed shape
272 break;
273
274 // point and small radius point
275 result.Insert({ Math.Cos(i) * m_fWidth * 0.5, 0, Math.Sin(i) * m_fLength * 0.5 });
276 result.Insert({ Math.Cos(i - 0.5 * angleSize) * m_fWidth * 0.5 * m_fStarInnerRadiusRatio, 0, Math.Sin(i - 0.5 * angleSize) * m_fLength * 0.5 * m_fStarInnerRadiusRatio });
277 }
278
279 return result;
280 }
281
282 //------------------------------------------------------------------------------------------------
285 protected void SetShapePoints(notnull IEntitySource entitySource, notnull array<vector> points)
286 {
287 int originalPointsCount = entitySource.GetObjectArray("Points").Count();
288 bool warned = false;
289
290 array<vector> pointsCopy = {};
291 pointsCopy.Copy(points);
292 if (m_bSnapToTerrain)
293 {
294 IEntity entity = m_API.SourceToEntity(entitySource);
295 if (entity)
296 {
297 foreach (int i, vector point : pointsCopy)
298 {
299 vector terrainPos = entity.CoordToParent(point);
300 float y;
301 if (!m_API.TryGetTerrainSurfaceY(terrainPos[0], terrainPos[2], y))
302 continue;
303
304 terrainPos[1] = y;
305 pointsCopy[i] = entity.CoordToLocal(terrainPos);
306 }
307 }
308 else
309 {
310 Print("Cannot snap shape(s) to terrain, entity not found from source", LogLevel.WARNING);
311 }
312 }
313
314 foreach (int i, vector point : pointsCopy)
315 {
316 m_API.CreateObjectArrayVariableMember(entitySource, null, "Points", "ShapePoint", i + originalPointsCount);
317 m_API.SetVariableValue(entitySource, { new ContainerIdPathEntry("Points", i + originalPointsCount) }, "Position", string.Format("%1 %2 %3", point[0], point[1], point[2]));
318 }
319
320 for (int i = originalPointsCount - 1; i >= 0; --i)
321 {
322 m_API.RemoveObjectArrayVariableMember(entitySource, null, "Points", i);
323 }
324 }
325}
326#endif // WORKBENCH
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
Get all prefabs that have the spawner data
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
proto external vector CoordToParent(vector coord)
proto external vector CoordToLocal(vector coord)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
SCR_FieldOfViewSettings Attribute
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.
TraceFlags
Definition TraceFlags.c:13