6 class SCR_ShapeAnalyserEntity : GeneratorBaseEntity
9 protected bool m_bDrawErrors;
11 [
Attribute(defvalue:
"30",
category:
"Shape",
desc:
"Maximum 2D angle (in degree) for a 10m line",
params:
"0 90 1", uiwidget: UIWidgets.Slider)]
12 protected float m_fMaxAngle;
14 [
Attribute(defvalue:
"10",
category:
"Shape",
desc:
"Maximum slope angle (in degree)",
params:
"0 90 1", uiwidget: UIWidgets.Slider)]
15 protected float m_fMaxSlope;
18 protected bool m_bDrawDebugShapes;
21 protected bool m_bPrintDebugInfo;
25 protected static ref array<ref Shape> s_aErrorShapes;
26 protected static ref array<ref Shape> s_aDebugShapes;
28 protected static const int ERROR_SHAPE_ANGLE_ARRAY_LENGTH = 50;
29 protected static const float ERROR_SHAPE_ANGLE_CHECK_LENGTH = 10.0;
31 protected static float SLOPE_ERROR_SHAPE_LENGTH = 30.0;
32 protected static float ANGLE_ERROR_LINE_LENGTH = 5.0;
34 protected static vector NORMAL_POINT_LINE_VECTOR =
"0 20 0";
35 protected static float NORMAL_POINT_LINE_HAT_SIZE = 1.0;
36 protected static vector TESSELATED_POINT_LINE_VECTOR =
"0 10 0";
37 protected static vector MIDDLE_POINT_LINE_VECTOR =
"0 15 0";
38 protected static int ERROR_SHAPE_COLOUR = Color.RED;
39 protected static int DEBUG_SHAPE_COLOUR = Color.MAGENTA;
40 protected static int DEBUG_SHAPE_COLOUR_IMPORTANT = Color.DARK_RED;
41 protected static ShapeFlags DEBUG_SHAPE_FLAGS = ShapeFlags.NOZBUFFER;
42 protected static ShapeFlags DEBUG_SHAPE_FLAGS_SPHERE = ShapeFlags.NOOUTLINE | ShapeFlags.TRANSP;
43 protected static int DEBUG_SHAPE_COLOUR_ALPHA = 0x66000000;
47 override bool _WB_OnKeyChanged(BaseContainer src,
string key, BaseContainerList ownerContainers, IEntity parent)
49 bool parentResult = super._WB_OnKeyChanged(src, key, ownerContainers, parent);
54 ShapeEntity parentShape = ShapeEntity.Cast(parent);
58 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
62 src = worldEditorAPI.EntityToSource(
this);
63 BaseContainerTools.WriteToInstance(
this, src);
65 Process(worldEditorAPI.EntityToSource(parent), parentShape);
71 protected override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
73 super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
75 Process(shapeEntitySrc, shapeEntity);
79 protected void Process(notnull IEntitySource shapeEntitySrc, notnull ShapeEntity shapeEntity)
84 s_aDebugShapes =
null;
85 s_aErrorShapes =
null;
87 if (m_bDrawDebugShapes)
91 DrawErrorShapes(shapeAnalyser);
93 if (m_bPrintDebugInfo)
94 PrintDebugInfo(shapeAnalyser);
102 array<ref SCR_Ray> pointRays = shapeAnalyser.GetPoints();
103 array<ref SCR_Ray> middlePointRays = shapeAnalyser.GetMiddlePoints();
104 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetTesselatedPoints();
106 int currentPointIndex = 0;
107 int currentMiddlePointIndex = 0;
108 int lastPointIndex = pointRays.Count() - 1;
109 int lastMiddlePointIndex = pointRays.Count() - 1;
110 SCR_Ray currentPoint = pointRays[0];
111 SCR_Ray currentMiddlePoint = middlePointRays[0];
113 foreach (
SCR_Ray pointRay : tesselatedPointRays)
115 if (pointRay == currentPoint)
117 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + NORMAL_POINT_LINE_VECTOR,
true);
118 AddDebugLine(pointRay.m_vPosition - pointRay.m_vDirection * NORMAL_POINT_LINE_VECTOR[1], pointRay.m_vPosition + pointRay.m_vDirection * NORMAL_POINT_LINE_VECTOR[1],
true);
120 if (currentPointIndex < lastPointIndex)
121 currentPoint = pointRays[currentPointIndex];
123 else if (pointRay == currentMiddlePoint)
125 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + MIDDLE_POINT_LINE_VECTOR,
true);
126 AddDebugLine(pointRay.m_vPosition - pointRay.m_vDirection * MIDDLE_POINT_LINE_VECTOR[1], pointRay.m_vPosition + pointRay.m_vDirection * MIDDLE_POINT_LINE_VECTOR[1],
true);
127 currentMiddlePointIndex++;
128 if (currentMiddlePointIndex < lastMiddlePointIndex)
129 currentMiddlePoint = middlePointRays[currentMiddlePointIndex];
133 AddDebugLine(pointRay.m_vPosition, pointRay.m_vPosition + TESSELATED_POINT_LINE_VECTOR);
139 protected void AddDebugLine(vector pointA, vector pointB,
bool isImportant =
false)
141 vector pointRays[2] = { pointA, pointB };
143 s_aDebugShapes.Insert(Shape.CreateLines(DEBUG_SHAPE_COLOUR_IMPORTANT, DEBUG_SHAPE_FLAGS, pointRays, 2));
145 s_aDebugShapes.Insert(Shape.CreateLines(DEBUG_SHAPE_COLOUR, DEBUG_SHAPE_FLAGS, pointRays, 2));
151 array<ref SCR_Ray> pointRays = shapeAnalyser.GetPoints();
152 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetTesselatedPoints();
158 float firstAngleDeg = Math.Atan2(pointRays[0].m_vDirection[0], pointRays[0].m_vDirection[2]) * Math.RAD2DEG;
159 float lastAnglesDeg[ERROR_SHAPE_ANGLE_ARRAY_LENGTH];
160 float lastAnglesDist[ERROR_SHAPE_ANGLE_ARRAY_LENGTH];
161 for (
int i; i < ERROR_SHAPE_ANGLE_ARRAY_LENGTH; i++)
163 lastAnglesDeg[i] = firstAngleDeg;
166 int lastTesselatedIndex = tesselatedPointRays.Count() - 1;
168 foreach (
int i,
SCR_Ray tesselatedPoint : tesselatedPointRays)
171 float slopeDeg = Math.Atan2(tesselatedPoint.m_vDirection[1], vector.DistanceXZ(tesselatedPoint.m_vDirection, vector.Zero)) * Math.RAD2DEG;
172 if (slopeDeg > m_fMaxSlope || slopeDeg < -m_fMaxSlope)
174 segment[0] = tesselatedPoint.m_vPosition;
175 segment[1] = segment[0] + vector.Up * SLOPE_ERROR_SHAPE_LENGTH;
176 s_aErrorShapes.Insert(Shape.CreateLines(Color.RED, ShapeFlags.NOZBUFFER, segment, 2));
180 float angleDeg = Math.Atan2(tesselatedPoint.m_vDirection[0], tesselatedPoint.m_vDirection[2]) * Math.RAD2DEG;
181 float angleRad = Math.Atan2(tesselatedPoint.m_vDirection[2], tesselatedPoint.m_vDirection[0]);
184 for (
int j = ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1; j >= 0; j--)
186 if (lastAnglesDist[j] == 0)
189 float angleDiffDeg = lastAnglesDeg[j] - angleDeg;
191 if (angleDiffDeg <= -180 || angleDiffDeg > 180)
192 angleDiffDeg = Math.Repeat(180 + angleDiffDeg, 360) - 180;
194 if (angleDiffDeg < 0)
197 if (angleDiffDeg > m_fMaxAngle)
199 segment[0] = tesselatedPoint.m_vPosition + { Math.Cos(angleRad - Math.PI_HALF), 0, Math.Sin(angleRad - Math.PI_HALF) } * (ANGLE_ERROR_LINE_LENGTH * 0.5);
200 segment[1] = tesselatedPoint.m_vPosition + { Math.Cos(angleRad + Math.PI_HALF), 0, Math.Sin(angleRad + Math.PI_HALF) } * (ANGLE_ERROR_LINE_LENGTH * 0.5);
201 s_aErrorShapes.Insert(Shape.CreateLines(Color.RED, ShapeFlags.NOZBUFFER, segment, 2));
205 totalDist += lastAnglesDist[j];
206 if (totalDist > ERROR_SHAPE_ANGLE_CHECK_LENGTH)
211 for (
int j, jMax = ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1; j < jMax; j++)
213 lastAnglesDeg[j] = lastAnglesDeg[j + 1];
214 lastAnglesDist[j] = lastAnglesDist[j + 1];
217 lastAnglesDeg[ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1] = angleDeg;
218 if (i != lastTesselatedIndex)
219 lastAnglesDist[ERROR_SHAPE_ANGLE_ARRAY_LENGTH - 1] = vector.DistanceXZ(tesselatedPoint.m_vPosition, tesselatedPointRays[i + 1].m_vPosition);
227 array<ref SCR_Ray> pointRays = shapeAnalyser.GetPoints();
228 array<ref SCR_Ray> tesselatedPointRays = shapeAnalyser.GetTesselatedPoints();
229 array<float> polygon2D = {};
231 array<float> stats = shapeAnalyser.GetStats();
234 float length2D = stats[statIndex++];
235 float length3D = stats[statIndex++];
236 float surface = stats[statIndex++];
237 float minAlt = stats[statIndex++], maxAlt = stats[statIndex++];
238 float minSlope = stats[statIndex++], maxSlope = stats[statIndex++];
240 float minAltATL =
float.INFINITY, maxAltATL = -
float.INFINITY;
244 foreach (
int i,
SCR_Ray pointRay : tesselatedPointRays)
248 length2D += vector.DistanceXZ(prevPointRay.m_vPosition, pointRay.m_vPosition);
249 length3D += vector.Distance(prevPointRay.m_vPosition, pointRay.m_vPosition);
252 tmp = pointRay.m_vPosition[1] - GetWorld().GetSurfaceY(pointRay.m_vPosition[0], pointRay.m_vPosition[2]);
260 prevPointRay = pointRay;
263 Print(
string.Format(
"%1 pointRays, %2 tesselated pointRays", pointRays.Count(), tesselatedPointRays.Count()), LogLevel.NORMAL);
264 Print(
"Length 2D/3D : " + length2D.ToString(lenDec: 2) +
"m / " + length3D.ToString(lenDec: 2) +
"m", LogLevel.NORMAL);
266 if (shapeAnalyser.IsClosed())
267 Print(
"Surface area : " + SCR_Math2D.GetPolygonArea(polygon2D).ToString(lenDec: 2) +
"m sq.", LogLevel.NORMAL);
269 Print(
"Surface area : not an area", LogLevel.NORMAL);
271 Print(
"Min/Max slope : [" + (minSlope * Math.RAD2DEG).ToString(lenDec: 2) +
"deg / " + (maxSlope * Math.RAD2DEG).ToString(lenDec: 2) +
"deg]", LogLevel.NORMAL);
272 Print(
"Min/Max altitude : [" + minAlt.ToString(lenDec: 2) +
"m / " + maxAlt.ToString(lenDec: 2) +
"m]", LogLevel.NORMAL);
273 Print(
"Min/Max alt ATL : [" + minAltATL.ToString(lenDec: 2) +
"m / " + maxAltATL.ToString(lenDec: 2) +
"m]", LogLevel.NORMAL);
282 void SCR_ShapeAnalyserEntity(IEntitySource src, IEntity parent)