Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ExportGeoDataPlugin.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2class SCR_GeoPointData
3{
4 vector m_vPosition;
5 vector m_vInTangent;
6 vector m_vOutTangent;
7
8 //------------------------------------------------------------------------------------------------
9 // constructor
10 void SCR_GeoPointData(vector position, vector inTangent = "0 0 0", vector outTangent = "0 0 0")
11 {
13 m_vInTangent = inTangent;
14 m_vOutTangent = outTangent;
15 }
16}
17
18enum SCR_EGeoExportType
19{
20 GEO_JSON,
21 SVG,
22}
23
24class SCR_GeoProperty
25{
26 string m_sName;
27 string m_sValue;
28
29 //------------------------------------------------------------------------------------------------
30 // constructor
31 void SCR_GeoProperty(string name, string value)
32 {
33 m_sName = name;
34 m_sValue = value;
35 }
36}
37
38class SCR_GeoExporter
39{
40 protected string m_sOutput;
41 protected float m_fXShift;
42 protected float m_fYShift;
43
44 protected ref FileHandle m_File;
45
46 static const string IS_CLOSED_PROPERTY = "IsClosed";
47 static const string LINE_COLOR_PROPERTY = "LineColor";
48 static const string ROAD_WIDTH_PROPERTY = "RoadWidth";
49
50 //------------------------------------------------------------------------------------------------
51 // constructor
52 void SCR_GeoExporter(string path, int xShift, int yShift)
53 {
54 m_sOutput = path;
55 m_fXShift = xShift;
56 m_fYShift = yShift;
57 }
58
59 //------------------------------------------------------------------------------------------------
62 bool Init()
63 {
64 m_File = FileIO.OpenFile(m_sOutput, FileMode.WRITE);
65 if (!m_File)
66 {
67 Print("Unable to open file to write " + m_sOutput, LogLevel.ERROR);
68 return false;
69 }
70
71 return true;
72 }
73
74 //------------------------------------------------------------------------------------------------
76 void Close()
77 {
78 if (m_File)
79 m_File.Close();
80 }
81
82 //------------------------------------------------------------------------------------------------
84 string GetPath()
85 {
86 return m_sOutput;
87 }
88
89 //------------------------------------------------------------------------------------------------
90 protected void Export(string line)
91 {
92 m_File.WriteLine(line);
93 }
94
95 // methods to override
96
97 //------------------------------------------------------------------------------------------------
99 void BeginExport();
100
101 //------------------------------------------------------------------------------------------------
107 void FeatureExport(string name, GeoShapeType shapeType, array<ref SCR_GeoPointData> points, array<ref SCR_GeoProperty> props);
108
109 //------------------------------------------------------------------------------------------------
111 void EndExport();
112}
113
114class SCR_GeoSVGExporter : SCR_GeoExporter
115{
116 protected float m_fXMax = 20000;
117 protected float m_fYMax = 20000;
118
119 protected static const string SVG_HEADER = "<svg version=\"1.1\" id=\"WE\""
120 + " xmlns=\"http:/" + "/www.w3.org/2000/svg\""
121 + " xmlns:xlink=\"http:/" + "/www.w3.org/1999/xlink\""
122 + " x=\"0px\" y=\"0px\" width=\"%1\" height=\"%2\" viewBox=\"0 0 %1 %2\">"; // %1 = xMax, %2 = yMax
123
124 //------------------------------------------------------------------------------------------------
125 override void BeginExport()
126 {
127 WorldEditor we = Workbench.GetModule(WorldEditor);
128
129 vector min, max;
130 we.GetTerrainBounds(min, max);
131 m_fXMax = max[0];
132 m_fYMax = max[2];
133
134 Export("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
135 Export("<!-- Generator: Enfusion, SVG Export Plug-In -->");
136
137 Export(string.Format(SVG_HEADER, (int)m_fXMax, (int)m_fYMax));
138 }
139
140 //------------------------------------------------------------------------------------------------
141 override void FeatureExport(string name, GeoShapeType shapeType, array<ref SCR_GeoPointData> points, array<ref SCR_GeoProperty> props)
142 {
143 if (shapeType == GeoShapeType.POINT)
144 {
145 vector pos;
146 foreach (SCR_GeoPointData point : points)
147 {
148 pos = point.m_vPosition;
149 Export(string.Format("<path d=\"M%1,%2\"/>", pos[0] + m_fXShift, pos[2] + m_fYShift));
150 }
151 }
152 else if (shapeType == GeoShapeType.POLYLINE)
153 {
154 int width = 1;
155
156 string polyline = "<polyline fill=\"none\" stroke=\"#000000\" stroke-width=\"" + width + "\" stroke-miterlimit=\"10\" ";
157 string pointsAttr = "points=\"";
158 vector pos;
159
160 foreach (int i, SCR_GeoPointData point : points)
161 {
162 if (i > 0)
163 pointsAttr += " ";
164
165 pos = point.m_vPosition;
166 pointsAttr += string.Format("%1,%2", pos[0] + m_fXShift, pos[2] + m_fYShift);
167 }
168
169 polyline += pointsAttr;
170 polyline += "\"/>";
171
172 Export(polyline);
173 }
174 else if (shapeType == GeoShapeType.POLYGON)
175 {
176 string polygon = "<polygon ";
177 string pointsAttr = "points=\"";
178 vector pos;
179
180 foreach (int i, SCR_GeoPointData point : points)
181 {
182 if (i > 0)
183 pointsAttr += " ";
184
185 pos = point.m_vPosition;
186 pointsAttr += string.Format("%1,%2", pos[0] + m_fXShift, pos[2] + m_fYShift);
187 }
188
189 polygon += pointsAttr;
190 polygon += "\"/>";
191
192 Export(polygon);
193 }
194 else if (shapeType == GeoShapeType.UNKNOWN) // Unknown is Spline for us atm
195 {
196 int width = 1;
197 foreach (SCR_GeoProperty geo : props)
198 {
199 if (geo.m_sName == ROAD_WIDTH_PROPERTY && name.ToType() && name.ToType().IsInherited(RoadGeneratorEntity))
200 {
201 width = geo.m_sValue.ToInt();
202 break;
203 }
204 }
205
206 string spline = "<path fill=\"none\" stroke=\"#000000\" stroke-width=\"" + width + "\" ";
207 string dAttr = "d=\"";
208 vector pos, nextPos;
209 vector inTangent, outTangent;
210
211 // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
212 int countMinus1 = points.Count() - 1;
213 foreach (int i, SCR_GeoPointData point : points)
214 {
215 if (i == countMinus1)
216 break;
217
218 SCR_GeoPointData nextPoint = points[i + 1];
219
220 // Negate pos.z and add m_fYMax because our Z maps to SVG Y and SVG has Y down but we have Z up.
221 // We need to mirror the Z(Y) axis. Also negate the tangent Y coordinate for the same reason.
222 pos = point.m_vPosition;
223 pos[0] = pos[0] + m_fXShift;
224 pos[2] = -pos[2] + m_fYMax + m_fYShift;
225 vector ot = point.m_vOutTangent;
226 ot[2] = -ot[2];
227 // Divide by 3 because Enfusion uses cubic Hermite splines, SVG uses cubic Bezier splines
228 // Division by 3 converts these two representations https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
229 outTangent = pos + ot / 3;
230
231 nextPos = nextPoint.m_vPosition;
232 nextPos[0] = nextPos[0] + m_fXShift;
233 nextPos[2] = -nextPos[2] + m_fYMax + m_fYShift;
234 vector it = nextPoint.m_vInTangent;
235 it[2] = -it[2];
236 // In tangent needs to be subtracted, in Enfusion we represent tangents always as "forward"
237 inTangent = nextPos - it / 3;
238
239 if (i == 0)
240 dAttr += string.Format("M %1 %2", pos[0], pos[2]);
241
242 dAttr += string.Format(" C %1 %2, %3 %4, %5 %6", outTangent[0], outTangent[2], inTangent[0], inTangent[2], nextPos[0], nextPos[2]);
243 }
244
245 spline += dAttr;
246 spline += "\"/>";
247
248 Export(spline);
249 }
250
251 }
252
253 //------------------------------------------------------------------------------------------------
254 override void EndExport()
255 {
256 Export("</svg>");
257 }
258}
259
260class SCR_GeoJSONExporter : SCR_GeoExporter
261{
262 protected int m_iIndex;
263
264 //------------------------------------------------------------------------------------------------
265 override void BeginExport()
266 {
267 Export("{\"type\": \"FeatureCollection\", \"features\": [");
268 }
269
270 //------------------------------------------------------------------------------------------------
271 override void FeatureExport(string name, GeoShapeType shapeType, array<ref SCR_GeoPointData> points, array<ref SCR_GeoProperty> props)
272 {
273 string type = "Point";
274
275 if (shapeType == GeoShapeType.UNKNOWN) // Spline is polyline for us
276 shapeType = GeoShapeType.POLYLINE;
277
278 if (shapeType == GeoShapeType.POLYLINE)
279 type = "LineString";
280 else
281 if (shapeType == GeoShapeType.POLYGON)
282 type = "Polygon";
283
284 if (m_iIndex > 0)
285 Export(","); // properties end
286
287 Export("{\"type\": \"Feature\",\"properties\":{"); // properties
288 Export("\"name\": \"" + name + "\"");
289
290 foreach (SCR_GeoProperty geo : props)
291 {
292 Export(",\"" + geo.m_sName + "\": \"" + geo.m_sValue + "\"");
293 }
294
295 Export("},\"geometry\":{\"type\": \"" + type + "\",\"coordinates\":"); // geometry/coordinates
296
297 if (shapeType == GeoShapeType.POLYGON)
298 Export("[["); // polygons can have holes
299
300 if (shapeType == GeoShapeType.POLYLINE)
301 Export("["); // polygons can have holes
302
303 int countMinus1 = points.Count() - 1;
304 foreach (int i, SCR_GeoPointData point : points)
305 {
306 if (i != countMinus1)
307 Export(string.Format("[%1,%2],", point.m_vPosition[0] + m_fXShift, point.m_vPosition[2] + m_fYShift));
308 else
309 Export(string.Format("[%1,%2]", point.m_vPosition[0] + m_fXShift, point.m_vPosition[2] + m_fYShift));
310 }
311
312 if (shapeType == GeoShapeType.POLYGON)
313 Export("]]"); // polygons can have holes end
314 if (shapeType == GeoShapeType.POLYLINE)
315 Export("]"); // polygons can have holes end
316
317 Export("}"); // coordinates/geometry end
318
319 Export("}"); // properties end
320
321 m_iIndex++;
322 }
323
324 //------------------------------------------------------------------------------------------------
325 override void EndExport()
326 {
327 Export("]}");
328 }
329}
330
335 name: PLUGIN_NAME,
336 description: "Export vector data as Geographical data",
337 shortcut: "5",
338 wbModules: { "WorldEditor" },
339 category: SCR_PluginCategory.WORLDEDITOR_IMPORT_EXPORT,
340 awesomeFontCode: 0xF56E)]
341class SCR_ExportGeoDataPlugin : WorldEditorPlugin
342{
343 [Attribute("$profile:export", UIWidgets.FileNamePicker, "Where to save exported file. Do not use file suffix, it will be created automatically.")]
344 protected string m_sExportPath;
345
346 [Attribute(SCR_EGeoExportType.GEO_JSON.ToString(), UIWidgets.ComboBox, desc: "Export type", enums: SCR_ParamEnumArray.FromString("Geo JSON;SVG"))]
347 protected SCR_EGeoExportType m_eType;
348
349 [Attribute("0")]
350 protected float m_fXShift;
351
352 [Attribute("0")]
353 protected float m_fYShift;
354
355 protected static const string PLUGIN_NAME = "Export Geographic Data";
356
357 //------------------------------------------------------------------------------------------------
358 override void Run()
359 {
360 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
361 int selectedCount = worldEditorAPI.GetSelectedEntitiesCount();
362 if (selectedCount < 1)
363 {
364 SCR_WorkbenchHelper.PrintDialog("You need to select at least one entity.", PLUGIN_NAME, level: LogLevel.ERROR);
365 return;
366 }
367
368 if (Workbench.ScriptDialog(PLUGIN_NAME, "", this) == 0)
369 return;
370
371 Export();
372 }
373
374 //------------------------------------------------------------------------------------------------
375 protected void Export()
376 {
377 if (m_sExportPath.IsEmpty())
378 {
379 SCR_WorkbenchHelper.PrintDialog("Export path must be set.", PLUGIN_NAME, level: LogLevel.ERROR);
380 return;
381 }
382
383 string relativeFilePath;
384 SCR_GeoExporter exporter;
385
386 if (m_eType == SCR_EGeoExportType.SVG)
387 {
388 relativeFilePath = m_sExportPath + ".svg";
389 exporter = new SCR_GeoSVGExporter(relativeFilePath, m_fXShift, m_fYShift);
390 }
391 else // SCR_EGeoExportType.GEO_JSON
392 {
393 relativeFilePath = m_sExportPath + ".json";
394 exporter = new SCR_GeoJSONExporter(relativeFilePath, m_fXShift, m_fYShift);
395 }
396
397 if (!exporter.Init())
398 {
399 SCR_WorkbenchHelper.PrintDialog("Unable to initialise the exporter.", PLUGIN_NAME, level: LogLevel.ERROR);
400 return;
401 }
402
403 Debug.BeginTimeMeasure();
404
405 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
406 WorldEditorAPI worldEditorAPI = worldEditor.GetApi();
407 WBProgressDialog progress = new WBProgressDialog("Processing", worldEditor);
408
409 int selectedCount = worldEditorAPI.GetSelectedEntitiesCount();
410 Print("GeoExport: exporting " + selectedCount + " entities...", LogLevel.NORMAL);
411 exporter.BeginExport();
412
413 IEntitySource entitySource;
414 array<ref SCR_GeoProperty> properties;
415 Color color;
416 ShapeEntity shapeEntity;
417 SplineShapeEntity splineEntity;
418 array<vector> positions;
419 array<ref SCR_GeoPointData> pointData;
420 typename typeName;
421
422 float prevProgress, currProgress;
423 for (int i; i < selectedCount; i++)
424 {
425 currProgress = i / selectedCount;
426 if (currProgress - prevProgress >= 0.01) // min 1%
427 {
428 progress.SetProgress(currProgress); // expensive
429 prevProgress = currProgress;
430 }
431
432 entitySource = worldEditorAPI.GetSelectedEntity(i);
433
434 string classname = entitySource.GetClassName();
435 string name;
436
437 properties = {};
438
439 // if a generator is selected, get its classname as name and take its parent shape
440 // (but for PrefabGeneratorEntity)
441
442 typeName = classname.ToType();
443 if (typeName && typeName.IsInherited(SCR_GeneratorBaseEntity) && !typeName.IsInherited(PrefabGeneratorEntity))
444 {
445 if (typeName.IsInherited(RoadGeneratorEntity))
446 {
447 float roadWidth;
448 entitySource.Get(SCR_GeoExporter.ROAD_WIDTH_PROPERTY, roadWidth);
449 properties.Insert(new SCR_GeoProperty(SCR_GeoExporter.ROAD_WIDTH_PROPERTY, roadWidth.ToString()));
450 }
451
452 name = classname;
453 entitySource = entitySource.GetParent();
454 }
455
456 // now, let's deal the entitySource itself
457
458 if (entitySource)
459 classname = entitySource.GetClassName();
460
461 if (entitySource && typeName == PolylineShapeEntity || typeName == SplineShapeEntity)
462 {
463 if (name.IsEmpty())
464 name = classname;
465
466 bool isClosed;
467 entitySource.Get(SCR_GeoExporter.IS_CLOSED_PROPERTY, isClosed);
468
469 GeoShapeType shapeType;
470 if (typeName == PolylineShapeEntity)
471 {
472 if (isClosed)
473 shapeType = GeoShapeType.POLYGON;
474 else
475 shapeType = GeoShapeType.POLYLINE;
476 }
477 else // Spline will be unknown for us, right now closed splines are not supported
478 { // but we can propage it through properties or change FeatureExport signature
479 shapeType = GeoShapeType.UNKNOWN;
480 }
481
482 entitySource.Get(SCR_GeoExporter.LINE_COLOR_PROPERTY, color);
483 string colorProperty = string.Format("%1,%2,%3,%4", Math.Round(color.R() * 255), Math.Round(color.G() * 255), Math.Round(color.B() * 255), Math.Round(color.A() * 255));
484 properties.Insert(new SCR_GeoProperty(SCR_GeoExporter.LINE_COLOR_PROPERTY, colorProperty));
485 // we can add isClosed to the properties so we can react in the exporters
486
487 shapeEntity = ShapeEntity.Cast(worldEditorAPI.SourceToEntity(entitySource)); // can't use entity since parent can be changed
488 splineEntity = SplineShapeEntity.Cast(shapeEntity);
489
490 positions = {};
491 shapeEntity.GetPointsPositions(positions);
492
493 pointData = {};
494
495 foreach (int j, vector position : positions)
496 {
497 vector pos = shapeEntity.CoordToParent(position);
498 vector inTangent;
499 vector outTangent;
500
501 if (splineEntity)
502 splineEntity.GetTangents(j, inTangent, outTangent); // tangents are relative to entity
503
504 pointData.Insert(new SCR_GeoPointData(pos, inTangent, outTangent));
505 }
506
507 if (isClosed && pointData.Count() > 2)
508 {
509 vector first = pointData[0].m_vPosition;
510 vector last = pointData[pointData.Count() - 1].m_vPosition;
511 if (first != last)
512 pointData.Insert(new SCR_GeoPointData(first));
513 }
514
515 exporter.FeatureExport(classname, shapeType, pointData, properties);
516 }
517 else // null parent or not a shape
518 {
519 pointData = { new SCR_GeoPointData(worldEditorAPI.SourceToEntity(entitySource).GetOrigin()) };
520
521 exporter.FeatureExport(classname, GeoShapeType.POINT, pointData, properties);
522 }
523 }
524
525 exporter.EndExport();
526 exporter.Close();
527
528 Debug.EndTimeMeasure(typename.EnumToString(SCR_EGeoExportType, m_eType) + " export");
529
530 SCR_WorkbenchHelper.PrintDialog("Geo data successfully exported to " + relativeFilePath, PLUGIN_NAME, level: LogLevel.NORMAL);
531 }
532
533 //------------------------------------------------------------------------------------------------
534 [ButtonAttribute("Export", true)]
535 protected int ButtonExport()
536 {
537 return 1;
538 }
539
540 //------------------------------------------------------------------------------------------------
541 [ButtonAttribute("Cancel")]
542 protected int ButtonCancel()
543 {
544 return 0;
545 }
546}
547#endif // WORKBENCH
string path
override void Init()
GenerateFlowMaps WorkbenchPlugin WorkbenchPluginAttribute("Regenerate river flow-maps", "Generate and save/overwrite river flow-maps", "", "", {"WorldEditor"}, "", 0xf773)
Definition FlowmapTool.c:59
vector m_vPosition
SCR_ECampaignBaseType m_eType
EDamageType type
vector position
LocalizedString m_sName
override void Run()
bool ButtonCancel()
ref FileHandle m_File
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
proto native void Close()
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
Definition Color.c:13
Definition Debug.c:13
proto external vector GetOrigin()
Definition Math.c:13
static ParamEnumArray FromString(string input)
static void PrintDialog(string message, string caption="", LogLevel level=LogLevel.WARNING)
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
FileMode
Mode for opening file. See FileSystem::Open.
Definition FileMode.c:14