Skip to content

Commit 18b949b

Browse files
committed
MissionSegmentizer: add Arc support
1 parent e2ea835 commit 18b949b

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

ExtLibs/Utilities/Mission/MissionSegmentizer.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ static public List<Segment> BuildSegments(MissionGraph graph, VehicleClass vehic
217217
segments.AddRange(GenerateSplineSegments(graph, edge, flags, overrideSrcPos));
218218
break;
219219
case SegmentKind.ArcTurn:
220-
// TODO: generate arc turn segment
221-
segments.Add(GenerateStraightSegment(graph, edge, flags, overrideSrcPos, overrideDestPos));
220+
segments.Add(GenerateArcSegment(graph, edge, flags, overrideSrcPos, overrideDestPos));
222221
break;
223222
}
224223
}
@@ -293,6 +292,64 @@ static bool TryGetSegmentKind(MissionNode dest, out SegmentKind kind)
293292
}
294293
}
295294

295+
static Segment GenerateArcSegment(MissionGraph graph, MissionEdge edge, SegmentFlags flags, PointLatLngAlt overrideSrcPos, PointLatLngAlt overrideDestPos, int samplesPerFullTurn = 40)
296+
{
297+
var src = edge.FromNode;
298+
var dest = edge.ToNode;
299+
var srcPos = overrideSrcPos ?? new PointLatLngAlt(src.Command);
300+
var destPos = overrideDestPos ?? new PointLatLngAlt(dest.Command);
301+
var chordDist = srcPos.GetDistance2(destPos);
302+
var arcAngleDeg = (double)dest.Command.p1;
303+
var halfAngleRad = arcAngleDeg * Math.PI / 360.0;
304+
var sinHalf = Math.Sin(halfAngleRad);
305+
306+
// Fall back to straight when sin(angle/2) is near zero (radius blows up). The threshold
307+
// is 1 degree from multiples of 360: inferred from the 359 in the MAVLink spec.
308+
const double halfTolerance = 0.5 * Math.PI / 180.0;
309+
if (Math.Abs(sinHalf) < Math.Sin(halfTolerance))
310+
{
311+
return GenerateStraightSegment(graph, edge, flags, overrideSrcPos, overrideDestPos);
312+
}
313+
314+
// Signed perpendicular offset from chord midpoint to arc center. Positive means center
315+
// is to the right of the chord (looking from src to dest) and negative means left.
316+
// tan(half) naturally handles all four cases: CW/CCW and <180/>180.
317+
var centerOffset = chordDist / (2.0 * Math.Tan(halfAngleRad));
318+
319+
var chordBearing = srcPos.GetBearing(destPos);
320+
var midpoint = srcPos.newpos(chordBearing, chordDist / 2.0);
321+
var center = midpoint.newpos(chordBearing + 90.0, centerOffset);
322+
var startBearing = center.GetBearing(srcPos);
323+
324+
// AP supports angles beyond 360 (e.g. 540 = full circle then semicircle). We strip off
325+
// full turns beyond two turns to bound the vertex counts; two turns (not one) so the
326+
// midpoint marker doesn't land on top of either endpoint.
327+
var absAngle = Math.Abs(arcAngleDeg);
328+
if (absAngle > 720.0)
329+
absAngle -= Math.Ceiling((absAngle - 720.0) / 360.0) * 360.0;
330+
int samples = Math.Max(2, (int)(samplesPerFullTurn * absAngle / 360.0) + 2);
331+
var drawSweep = Math.Sign(arcAngleDeg) * absAngle;
332+
333+
var radius = chordDist / (2.0 * Math.Abs(sinHalf));
334+
var path = new List<PointLatLngAlt>(samples + 1);
335+
for (int i = 0; i <= samples; i++)
336+
{
337+
double bearing = startBearing + drawSweep * (i / (double)samples);
338+
path.Add(center.newpos(bearing, radius));
339+
}
340+
341+
var midBearing = startBearing + drawSweep * 0.5;
342+
return new Segment
343+
{
344+
Kind = SegmentKind.ArcTurn,
345+
Flags = flags,
346+
StartNode = src,
347+
EndNode = dest,
348+
Path = path,
349+
Midpoint = center.newpos(midBearing, radius),
350+
};
351+
}
352+
296353
static Segment GenerateStraightSegment(MissionGraph graph, MissionEdge edge, SegmentFlags flags, PointLatLngAlt overrideSrcPos, PointLatLngAlt overrideDestPos)
297354
{
298355
var src = edge.FromNode;

0 commit comments

Comments
 (0)