Solved

Simple rotational procedure in mode 13h

Posted on 1998-12-19
12
255 Views
Last Modified: 2010-04-16
Hello,

My objective is to have a SIMPLE (keyword, not FAST) procedure written entirely in pascal.  I've found most rotate procedures on the 'net are written in asm.  Unfortunately, i have no knowledge there.  

Anyhow, I've seen many methods discussed for doing this.
Apparently, the method of just transferring a pixel from the picture to it's new location leaves holes.  But if you work backwards, pic the new location, then look back to see what color pixel is there, you can fix this problem nicely.  Or something along those lines.  

The farthest i've been able to get in any sort of rotational thing would be to draw a circle.  Or should i say, rotate a point a set distance around the center.  (I hear there is a different 'circle' formula)  

At any rate, if someone could supply a SOURCE for doing this, it would be great.  You can really just put pixel's onto the screen and rotate them, I can always put in an image file loader later.  

Most importantly, this has to be SIMPLE please.  No need to use lookup tables or anything, calculate the sin/cos everytime.  My goal here is to understand it.  

THANKS
0
Comment
Question by:siralop
  • 5
  • 5
  • 2
12 Comments
 
LVL 3

Expert Comment

by:vikiing
ID: 1216629
To rotate a point (in the plane), you must know:
a) Coordinates of the point which your point will rotate around
   (this is, "COR" = the "center of rotation").
b) The angle you want to rotate.

To apply Pithagoras' theorem, the COR must be at (0,0); thus, the trick is to calculate the differences (in coordinates units) respect to COR; after that, you can apply the angle, and, finally, to adjust the point with original displacement. That will give you the new coordinates of point.

Let's call Xrot and Yrot the center of rotation, and Ang, the angle to rotate.

Then, you can evaluate new location of point, which is located at Xp and Yp, with this algorithm:

Evaluate displacements as the difference between point and COR [coordinates]. Dx & Dy represent the "virtual" point you'll rotate as if COR really were at (0,0):
Dx:=Xp-Xrot; Dy:=Yp-Yrot;

Rotate the so obtained values. Watch out: first expression states "Cos-Sin"; second one says "Sin+Cos":
Xp:=Cos(Ang)*Dx - Sin(Ang)*Dy;
Yp:=Sin(Ang)*Dx + Cos(Ang)*Dy;

Finally, restore displacements:
Xp:=Xp+Xrot; Yp:=Yp+Yrot;

Xp and Yp have now the coordinates of new location of original point.


0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1216630
Here is an answer I posted to a similar question a few months ago:
----------------------------------------------------------

program rotate;

{written by scrapdog  10/6/98}

uses crt;

const
  MAX_WIDTH = 32;
  MAX_HEIGHT = 32;

type
  TSprite = record
              Width, Height :integer;
              Picture       :array[0..MAX_WIDTH, 0..MAX_WIDTH] of byte;
            end;

procedure Rotate(var Sprite :TSprite;
                 var NewSprite :TSprite;
                     Theta :integer);
var
  nx,ny,x,y,i,j  :integer;
  CosA, SinA  :real;
  CenterX, CenterY :integer;
begin
  SinA := sin(Theta/180 * pi);
  CosA := cos(Theta/180 * pi);
  CenterX := Sprite.Width div 2;
  CenterY := Sprite.Height div 2;
  NewSprite.Width := Sprite.Width;
  NewSprite.Height := Sprite.Height;
  for i := 0 to Sprite.Width - 1 do
    for j := 0 to Sprite.Height - 1 do begin
      x := i - CenterX;
      y := j - CenterY;
      nx := (CenterX+(round(x*CosA-y*SinA)) mod Sprite.Width);
      ny := (CenterY+(round(x*SinA+y*CosA)) mod Sprite.Height);
      NewSprite.Picture[i,j] := Sprite.Picture[nx,ny];
    end;
end;

procedure Scale(var Sprite :TSprite;
                var NewSprite  :TSprite;
                Scale :Real);
var
  nx,ny,x,y,i,j  :integer;
  CenterX, CenterY, NewCenterX, NewCenterY :integer;
begin
  scale := abs(scale);
  if scale = 0 then exit;
  NewSprite.Width := round(Scale*Sprite.Width);
  NewSprite.Height := round(Scale*Sprite.Height);
  CenterX := Sprite.Width div 2;
  CenterY := Sprite.Height div 2;
  NewCenterX := NewSprite.Width div 2;
  NewCenterY := NewSprite.Height div 2;
  for i := 0 to NewSprite.Width - 1 do
    for j := 0 to NewSprite.Height - 1 do begin
      x := i- NewCenterX;
      y := j- NewCenterY;
      nx := (CenterX+round(x/scale) mod Sprite.Width);
      ny := (CenterY+round(y/scale) mod Sprite.Height);
      NewSprite.Picture[i,j] := Sprite.Picture[nx,ny]
    end;
end;



procedure DisplaySprite(var Sprite :TSprite);
var i,j :integer;
begin
  for i := 0 to Sprite.Height do begin
    for j := 0 to Sprite.Width do write(Chr(Sprite.Picture[j,i]),Chr(Sprite.Picture[j,i]));
    writeln;
  end;
end;

var Sprite, NewSprite :TSprite;
    i,j  :integer;
begin
  Sprite.Width := 10;
  Sprite.Height := 10;
  NewSprite.Width := 10;
  NewSprite.Height := 10;
  for i := 0 to 20 do for j := 0 to 9 do
    if (random(10)>7) then Sprite.Picture[j,i] := 219
    else Sprite.Picture[j,i] := 32;
  DisplaySprite(Sprite);
  writeln('------');
  for i := 0 to 360 do begin
    Rotate(Sprite, NewSprite, i);
    clrscr;
    DisplaySprite(NewSprite);
    Delay(1);
  end;
  writeln('press enter');
  readln;
  clrscr;
  for i := 1 to 20 do begin
    Scale(Sprite, NewSprite, i/10);
    clrscr;
    DisplaySprite(NewSprite);
    Delay(30);
  end;
  for i := 20 downto 1 do begin
    Scale(Sprite, NewSprite, i/10);
    clrscr;
    DisplaySprite(NewSprite);
    Delay(30);
  end;
  writeln('press enter');
  readln;
end.


------------------------------------------

This program is a demonstration of how to rotate sprites.  Text mode sprites are used to demonstrate the functions, but rotation of graphics in any mode is done the same way (MCGA, text, whatever).

The only functions you need to be concerned with are the Rotate and Scale functions.  You did not specify to me how you were storing your sprite, so I made my own data type TSprite (which you can change to fit your needs).

TSprite holds the height and width of the sprite in pixels, and the array "Picture" holds the pixels for the sprite.

To map one sprite onto another (rotated), call the rotate function.  The sprite will be rotated around its center, and the new sprite will have the same dimensions as the old one.

Example:

var
  Sprite, RotatedSprite :TSprite;

Rotate(Sprite, RotatedSprite, 45);  {Copys the Sprite onto Rotated Sprite and rotates it by 45 degrees}

Note that this doesn't change Sprite in anyway.  It just puts the rotated version of the sprite into RotatedSprite.  (Rotating the ACTUAL sprite will result in loss of data eventually, so it is always good to keep a record of the original unrotated sprite, so thats why the function works this way).

Note that the number of degrees must be integer.  It can be positive or negative to indicate the diretion of rotation.


Scale works almost the same way.  It copys one sprite onto another and scales it.

Scale(Sprite, ScaledSprite, 1.5);

causes ScaledSprite to be a 1.5x copy of Sprite.


You can use the code above in your program (you might have to make some changes due to how your sprite data type works).  All you need are the Rotate and Scale procedures (and maybe the type declaration for TSprite).  The rest of the program is to demonstrate how they work and will not be needed by your program.  The only thing the rotate and scale functions handle are the mathematical transformations...it does not display the graphics.  Therefore this will work in any graphics mode.  Displaying the new rotated/scaled sprite is up to you.

Example:

Rotate(Sprite, RotatedSprite, 45);
DisplayOnScreen(RotatedSprite);    {one of your procedures, or however you want to do it}


Additional note
----------------
When rotating a sprite, it is necessary that the height and width of the sprite are the same (or close), or there will be a loss of data.  Make sure the height and width are large enough so edges won't be cut out when you rotate it.  Making the height/width 1.414 times higher than the actual height/width of the graphic will accomplish this.

Example: if your graphic is 10 x 10, make the sprite height and width 14 x 14, and fill this outer edge with transparent pixels (value of 0,etc.), and in your display procedure don't map the transparent pixels.  Just a suggestion.



There are better ways of doing this, but this is the simplest.

scrapdog
 
---------------------------------------------


Vikiing's explanation is mathematically correct...however, in practice, it is good to copy pixels from one image to another as shown above in order to avoid gaps in the image.

0
 

Author Comment

by:siralop
ID: 1216631
I have a couple questions regarding this.  In the below code, I had it rotate a point around 160,100 the center of the screen.  Theoretically, this should draw a circle, instead it does a sorta inaccurate type circle (hopefully you'll run the code i made and see what i mean)  Maybe i did something wrong, or there's a reason why is ?should? do that?  I'll add the formula I used to draw and rotate a point around the center of the screen.  The one I used previously seems more accurate.  But apparently, it would still leave wholes when rotation a full image, and that's what I want resolved.  Here is what I made out of your formulas:

Var    CoR,Ang,XRot,YRot,XP,YP,Dx,Dy:Integer;
Begin
InitMCGA;    {into graphics}
XRot:=160;YRot:=100; {I chose the center of the screen}
XP:=150;YP:=90;{This gives like a 10 pixel radius}

For Ang:=1 To 360 Do
Begin
  Dx:=XP-XRot;
  Dy:=YP-YRot;
  XP:=Round((Cos(Ang)*Dx) - (Sin(Ang)*Dy));
  YP:=Round((Sin(Ang)*Dx) + (Cos(Ang)*Dy));
  XP:=XP+XRot;
  YP:=YP+YRot;
  SetPixel(XP,YP,13,$a000); {put the pixel to screen}
End;
readkey;
End.

Hopefully you'll run that and see what I mean. Now on with what I used previously:

Var   XRot,YRot,Radius,NX,NY,Ang:Integer;
      Sinus,Cosine:Real;

Begin
Radius:=10;
XRot:=160;
YRot:=100;

For I:=1 To 360 Do
Begin
  Cosine:=Cos(Ang*(PI/180));
  Sinus:=Sin(Ang*(PI/180));
  NX:=Round((Radius*Sinus)-(Radius*Cosine));
  NY:=Round((Radius*Cosine)+(Radius*Sinus));
  NX:=NX+XRot;
  NY:=NY+YRot;
  SetPixel(NX,NY,10,$a000);
End;

If you try that, you'll see it's more 'accurate' as i'm calling it.  That apparently still gives holes to a picture when rotated, and I can see why it would.  Also, the radius thing is not accurate, it's always a little smaller than what appears.  I'm not sure what causes that.  I'm just really looking for something that will ACCURATELY (meaning, without holes) rotate a picture, but as always, simple like the above codes

Any ideas?



0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1216632
In the first one, you didn't convert degrees to radians.

(Ang*pi/180) converts degrees to radians.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1216633
To rotate something an entire image:

Say your images are 100x100:

In pseudocode:

for newx = 0 to 100
  for newy = 0 to 100
    oldx = formula (newx)   {applied backwards}
    oldy = formula (newy)   {also applied backwards!!}
    newimage.pixel[newx,newy] = oldimage.pixel[oldx,oldy]

This method guarantees that there will be no holes.

0
 

Author Comment

by:siralop
ID: 1216634
I have a couple questions regarding this.  In the below code, I had it rotate a point around 160,100 the center of the screen.  Theoretically, this should draw a circle, instead it does a sorta inaccurate type circle (hopefully you'll run the code i made and see what i mean)  Maybe i did something wrong, or there's a reason why is ?should? do that?  I'll add the formula I used to draw and rotate a point around the center of the screen.  The one I used previously seems more accurate.  But apparently, it would still leave wholes when rotation a full image, and that's what I want resolved.  Here is what I made out of your formulas:

Var    CoR,Ang,XRot,YRot,XP,YP,Dx,Dy:Integer;
Begin
InitMCGA;    {into graphics}
XRot:=160;YRot:=100; {I chose the center of the screen}
XP:=150;YP:=90;{This gives like a 10 pixel radius}

For Ang:=1 To 360 Do
Begin
  Dx:=XP-XRot;
  Dy:=YP-YRot;
  XP:=Round((Cos(Ang)*Dx) - (Sin(Ang)*Dy));
  YP:=Round((Sin(Ang)*Dx) + (Cos(Ang)*Dy));
  XP:=XP+XRot;
  YP:=YP+YRot;
  SetPixel(XP,YP,13,$a000); {put the pixel to screen}
End;
readkey;
End.

Hopefully you'll run that and see what I mean. Now on with what I used previously:

Var   XRot,YRot,Radius,NX,NY,Ang:Integer;
      Sinus,Cosine:Real;

Begin
Radius:=10;
XRot:=160;
YRot:=100;

For I:=1 To 360 Do
Begin
  Cosine:=Cos(Ang*(PI/180));
  Sinus:=Sin(Ang*(PI/180));
  NX:=Round((Radius*Sinus)-(Radius*Cosine));
  NY:=Round((Radius*Cosine)+(Radius*Sinus));
  NX:=NX+XRot;
  NY:=NY+YRot;
  SetPixel(NX,NY,10,$a000);
End;

If you try that, you'll see it's more 'accurate' as i'm calling it.  That apparently still gives holes to a picture when rotated, and I can see why it would.  Also, the radius thing is not accurate, it's always a little smaller than what appears.  I'm not sure what causes that.  I'm just really looking for something that will ACCURATELY (meaning, without holes) rotate a picture, but as always, simple like the above codes

Any ideas?



0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 

Author Comment

by:siralop
ID: 1216635
That looks great scrapdog.  Thanks!  Thanks to Vikiing too. Scrapdog:  If you want to just post an answer, i'll give you the points.  You helped out BIG time.

Thanks again,

Siralop
0
 
LVL 5

Accepted Solution

by:
scrapdog earned 150 total points
ID: 1216636
Thanks!!  Feel free to ask for further help.
0
 

Author Comment

by:siralop
ID: 1216637
That looks great scrapdog.  Thanks!  Thanks to Vikiing too. Scrapdog:  If you want to just post an answer, i'll give you the points.  You helped out BIG time.

Thanks again,

Siralop
0
 
LVL 3

Expert Comment

by:vikiing
ID: 1216638
The reason why you see an ellipse is because actual width and height units are not the same. If you draw a line from (0,0) to (100,0) (a 100-pixels horizontal line), you will see the difference in length with a line drawed from (0,0) to (0,100) (a 100-pixels vertical line).

Respect to your main goal (simplicity), I've explained you the simplest way to rotate a point; I (wrongly) suposed the rest was up to you.

Anyway, glad to have helped you.

0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1216639
siralop:  The relationship of the height/width of pixels is the aspect ratio...an aspect ratio of 1 means square pixels.  320x200 (13h) has pixels which are slightly wider than they are high...I think that 640x480 has square pixels, but don't quote me on that...
0
 

Author Comment

by:siralop
ID: 1216640
Thanks, I already knew that though (:  I think it's a 3/2 ratio between height/width.  I imagine they would be square pixels for a 640x480 res, because when you think about it:  640x400 would be the same 'aspect ratio', just half the size pixels.  So if you squish more pixels in on the bottom, it would make each pixel a little less high, probably making them out to be square.  

Nevertheless, thanks for commenting on that
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

HOW TO: Upload an ISO image to a VMware datastore for use with VMware vSphere Hypervisor 6.5 (ESXi 6.5) using the vSphere Host Client, and checking its MD5 checksum signature is correct.  It's a good idea to compare checksums, because many installat…
Restoring deleted objects in Active Directory has been a standard feature in Active Directory for many years, yet some admins may not know what is available.
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now