← Home SEEBIE90 DOCS
The GTG Safe Zone mod is published under the Arma Public License (APL)Example code is free to useSuggestions: Discord to the author

GTG Safe Zone

The alarm ring

Around the zone lies a band of, say, 150 m. An enemy who enters it raises the alarm. The band follows every corner of the outline, notches included, and it must never turn into a puzzle of its own.

In plain wordsThe ring is not a second outline. It is the question: is this position within so many metres of the nearest edge?

The obvious version: a bigger polygon

The first idea is to push every edge outwards by the ring width and treat the result as a second polygon. For a convex shape that is fine. For a concave one it is not: when a notch is narrower than twice the ring width, the pushed-out edges cross each other and leave loops that somebody would have to cut away. The shape you test against is then broken exactly where the zone is most interesting.

The robust version: a distance

A position is in the ring when it is outside the zone and not further from the outline than the ring width. That is almost the same set of positions as the pushed-out polygon, only without ever constructing it – mathematically, the outline grown by a circle. It cannot degenerate, and it is cheaper. The one visible difference is that the outer corners are round instead of sharp.

Example code – free to useMyAD_Geometry.c
// Shortest distance from a point to a line segment, in the XZ plane.
static float DistanceToSegmentXZ(vector p, vector a, vector b)
{
	float abx = b[0] - a[0];
	float abz = b[2] - a[2];
	float apx = p[0] - a[0];
	float apz = p[2] - a[2];

	float lenSq = abx * abx + abz * abz;

	// How far along the segment the point projects, clamped to 0..1 - outside the segment
	// the nearest point is then simply an end. A degenerate segment (two identical points)
	// keeps t at 0 and measures to a: correct, and no division by zero.
	float t = 0;
	if (lenSq > 0.0001)
		t = Math.Clamp((apx * abx + apz * abz) / lenSq, 0, 1);

	float dx = apx - t * abx;
	float dz = apz - t * abz;
	return Math.Sqrt(dx * dx + dz * dz);
}

// Shortest distance to the outline, whether the point is inside or outside.
static float DistanceToPolygonEdgeXZ(vector p, notnull array<vector> poly)
{
	int count = poly.Count();
	if (count < 2)
		return float.MAX;

	float best = float.MAX;
	int j = count - 1;
	for (int i = 0; i < count; i++)
	{
		float d = DistanceToSegmentXZ(p, poly[j], poly[i]);
		if (d < best)
			best = d;
		j = i;
	}

	return best;
}

The question the alarm scan asks is then two lines, in the same order as every other check in the zone – cheap tests first:

Example code – free to use
bool IsInsideOrInRing(vector pos)
{
	if (!m_bValid || !m_bEnabled || !IsHeightInRange(pos))
		return false;

	if (MyAD_Geometry.IsPointInPolygonXZ(pos, m_aOutline))
		return true;

	return MyAD_Geometry.DistanceToPolygonEdgeXZ(pos, m_aOutline) <= m_fRingWidth;	// e.g. 150
}
NoteThe test and the drawing are two different jobs. A distance test needs no outline of the ring, but a line on a map does. Drawing it brings the offset back: the mod's map display extends the pushed-out edges to their intersection at every corner, trims very sharp corners, and accepts that at a notch the drawn line can overshoot the true boundary. What counts is the distance test; the line is only the picture.

Neutral parties do not trip it

Only a real enemy of the zone's owner raises the alarm. Somebody who is neither friendly nor hostile may stand in the ring as long as they like. Otherwise civilian traffic on a road at the edge would keep the zone armed permanently.

Example code on this page is free to use in your own projects (credit appreciated). The GTG Safe Zone mod is published under the Arma Public License (APL). Suggestions: Discord. Arma Reforger / Enfusion are trademarks of Bohemia Interactive.
Impressum · Privacy Policy