Jerry Dodge
Jerry Dodge

Reputation: 27286

Is there a function to shift degrees of a circle past 0?

I'm looking for a function somewhere in Delphi XE2 similar to Inc() which allows me to add/subtract a number of degrees from a current number of degrees and result in the new degrees. For example, if I have a point currently at 5 degrees around a circle, and I want to subtract 10, I should not get -5 degrees, but rather 355 (360 - 5). Same as adding past 360 - it should go back to 0 when it reaches 360.

Is there anything like this already in Delphi so I don't have to re-write it? Perhaps in the Math unit?

Upvotes: 6

Views: 589

Answers (5)

LU RD
LU RD

Reputation: 34919

uses
  System.SysUtils,Math;

Function WrapAngle( angle : Double) : Double;
Const
  modAngle : Double = 360.0;
begin
  Result := angle - modAngle*Floor(angle/modAngle);
end;

begin
  WriteLn(FloatToStr(WrapAngle(-5)));
  WriteLn(FloatToStr(WrapAngle(5-720)));
  WriteLn(FloatToStr(WrapAngle(360)));
  ReadLn;    
end.

Produces result:

355
5 
0

Update:

As @Giel found, in XE3 there is a new function DegNormalize() which does the job. Even about 25% faster. The trick is to replace the Floor() call with an Int() instead, and if the result is negative, add modAngle to the result.

Upvotes: 10

NGLN
NGLN

Reputation: 43669

procedure WrapAngle(var Degs: Integer);
begin
  Degs := Degs mod 360;
  if Degs < 0 then
    Inc(Degs, 360);
end;

Upvotes: 1

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

function WrapAngle(Value: Integer): Integer;
begin
  Result := Value mod 360;
  if Result < 0 then
    Inc(Result, 360);
end;

Upvotes: 4

David Heffernan
David Heffernan

Reputation: 613322

The code I use to perform this task is:

function PosFrac(x: Double): Double;
(* PosFrac(1.2)=0.2 and PosFrac(-1.2)=0.8. *)
begin
  Result := Frac(x); (* Frac(x)=x-Int(x) *)
  if Result<0.0 then begin
    Result := 1.0+Result;
  end;
end;

function ModR(const x, y: Double): Double;
(* ModR(1.2,1)=0.2 and ModR(-1.2,1)=0.8 *)
var
  absy: Double;
begin
  if y=0.0 then begin
    Result := 0.0;
  end else begin
    absy := abs(y);
    Result := PosFrac(x/absy)*absy;
  end;
end;

function Mod360(const x: Double): Double;
begin
  Result := ModR(x, 360.0);
end;

This code will bring all angles into the range 0 to 360. For example:

Writeln(Round(Mod360(5-10)));
Writeln(Round(Mod360(5-360)));
Writeln(Round(Mod360(5-720)));
Writeln(Round(Mod360(5+720)));

outputs:

355
5
5
5

Upvotes: 3

bummi
bummi

Reputation: 27385

I don't know any, but I'd prefer using a more general solution anyway ...

Procedure IncOverFlow(var Value:Double;Difference:Double;Limit:Double=360);
begin
   Value := Value + Difference;
   While Value < 0 do Value := Value + Limit;
   While Value >= Limit do Value := Value -Limit;
end;

Upvotes: 1

Related Questions