UserControl1.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. ////////////////////////////////////////////////////////////////////////////
  2. // Copyright : Robert Hartman
  3. // Date: 2/25/2006
  4. //
  5. // Email : hart_dev@yahoo.com
  6. //
  7. // This file may be redistributed unmodified by any means PROVIDING it is
  8. // not sold for profit without the authors written consent, and
  9. // providing that this notice and the authors name is included.
  10. //
  11. // This file is provided 'as is' with no expressed or implied warranty.
  12. // The author accepts no liability for damages caused by the use of this software.
  13. ////////////////////////////////////////////////////////////////////////////
  14. using System;
  15. using System.Collections.Generic;
  16. using System.ComponentModel;
  17. using System.Drawing;
  18. using System.IO;
  19. using System.Data;
  20. using System.Text;
  21. using System.Reflection;
  22. using System.Drawing.Drawing2D;
  23. using System.Windows.Forms;
  24. namespace PictureBook
  25. {
  26. public partial class TurnThePageControl : UserControl
  27. {
  28. private Image LeftBinder;
  29. private Image RightBinder;
  30. // tickSpeed, is the timer interval in milliseconds. Controls the frame rate
  31. private int tickSpeed = 15;
  32. // step size in the X direction. Controls the number of frames
  33. private int MOVE_X_BY = 10;//每次移动的像素——周公
  34. /* Debug constants and illustrative points*/
  35. // INCLUDE_DRAW_GRAPHICS_PATH will draw the outline of
  36. // graphic paths during the page turn effect
  37. private const bool INCLUDE_DRAW_GRAPHICS_PATH = false;
  38. // INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION will
  39. // show the underside of the page being turned.
  40. // Setting this value to false will not show the
  41. // the underneath side of the page being animated.
  42. private const bool INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION = true;
  43. // Use PixelOffsetMode. This sets the
  44. // PixelOffsetMode to PixelOffsetMode.Half
  45. // of the combined bitmap. If this mode is not
  46. // used I simply add or subtract 1 pixel when
  47. // drawing areas B and C the page being turned
  48. private const bool USE_PIXEL_MODE_OFFSET = false;
  49. // INCLUDE_SPIRAL_SPINE will include the Spiral binder
  50. // in the animation
  51. private const bool INCLUDE_SPIRAL_SPINE = true;
  52. // INCLUDE_DRAW_HOTSPOT will cause a small half circle around
  53. // the point of page rotation.
  54. private const bool INCLUDE_DRAW_HOTSPOT = false;
  55. private int PAGE_SPINE; // Middle of the two pages
  56. private int BOOK_WIDTH;
  57. private int PAGE_BOTTOM; // Y coordinate of the bottom of the page
  58. private int PAGE_WIDTH; // Width of the page
  59. private int PAGE_HEIGHT; // Height of the page
  60. //Sample Bitmaps, used to make the pages of this Picture Book
  61. private Bitmap[] bitmaps; // Container for all pages in this book
  62. // Combined offscreen image
  63. private Bitmap CurrentShownBitmap=null;
  64. // Represents the page under the current page being turned
  65. private Bitmap NextUnderBitmap;
  66. // Represents the page on the underside of the page being turned
  67. private Bitmap UndersideBitmap;
  68. // State variable indicating that the picture book is currently
  69. // animating a page turn
  70. private bool isInAnimation;
  71. // Track if the current line of symmetry defines a trapezoid of a triangle.
  72. private bool isPathTrapezoid;
  73. // Start on page 3 (left page) and page 4 (right page)
  74. private int currentLeftPage = 3;
  75. private int currentRightPage = 4;
  76. private int animatingToLeftPage;
  77. private int animatingToRightPage;
  78. // pathAngleInflection is the angle at which the underside
  79. // path is changed from a triangle to a trapezoid
  80. //private double pathAngleInflection;
  81. // X distance where the path is change
  82. // from a triangle to a trapezoid.
  83. //private int xWidthInflection;
  84. // Current location from the edge of the page.
  85. private int currentXdistanceFromEdge = 0;
  86. // animationTurnType indicates to algorithms if the
  87. // page is turning to the right or to the left
  88. private TurnType animationTurnType;
  89. // This is the location of the line of symmetry
  90. private Circle hotSpot;
  91. // This value allows the control to have room at the
  92. // top so the user can see the top of the pages as they
  93. // are turned.
  94. private int heightOffsetForAnimationTop;
  95. public TurnThePageControl()
  96. {
  97. InitializeComponent();
  98. // Get this type's assembly
  99. Assembly assem = this.GetType().Assembly;
  100. /* Get the Left and Right binders from project resources */
  101. Stream stream =
  102. assem.GetManifestResourceStream(
  103. "PictureBook.Resources.SingleBinderLeft.bmp"
  104. );
  105. // Load the bitmap from the stream
  106. LeftBinder = new Bitmap(stream);
  107. stream =
  108. assem.GetManifestResourceStream(
  109. "PictureBook.Resources.SingleBinderRight.bmp"
  110. );
  111. RightBinder = new Bitmap(stream);
  112. heightOffsetForAnimationTop = 0;
  113. isPathTrapezoid = false;
  114. }
  115. // Simply controls the speed of the animation
  116. public int TickSpeed
  117. {
  118. get
  119. {
  120. return tickSpeed;
  121. }
  122. set
  123. {
  124. tickSpeed = value;
  125. }
  126. }
  127. //Simply controls the number of times that the animation is drawn
  128. public int MoveXBy
  129. {
  130. get
  131. {
  132. return MOVE_X_BY;
  133. }
  134. set
  135. {
  136. if (value <= 1)
  137. {
  138. MOVE_X_BY = 2;
  139. }
  140. else
  141. {
  142. MOVE_X_BY = value;
  143. }
  144. }
  145. }
  146. //Gives the control a little more room so clipping of the underside page
  147. //does not happen.
  148. public int HeightAdjustment
  149. {
  150. get
  151. {
  152. return heightOffsetForAnimationTop;
  153. }
  154. set
  155. {
  156. heightOffsetForAnimationTop = value;
  157. }
  158. }
  159. #region NOT_USED
  160. // This function will calculate the angle and the distance at
  161. // which the page turn region changes from a triangle to a
  162. // trapezoid . These values are very important in the
  163. // calculation of various graphic paths. The paths will be
  164. // used to control the animation of a page.
  165. //
  166. // a= maxTriangleAngle = angle at which for a given
  167. // distance x=maxTriangleDistanceFromPE, the height of the
  168. // triangle will be the height of the bitmap. It is assumed
  169. // that when x = 0 the angle a below is 45 degrees. As the
  170. // distance x increases the angle a will
  171. // also increase by a function of x to 90 degrees.
  172. //
  173. //
  174. // + Bitmap Height
  175. // + |
  176. // + |
  177. // + |
  178. // + |
  179. // +a____x___|
  180. // maxTriangleDistanceFromPE
  181. //
  182. // Bitmap Height - is the height of the bitmap. It lies along
  183. // the page edge.
  184. //
  185. // x - is the distance from the page edge. As x is increased
  186. // so will the calculated height for the triangle.
  187. // When the calculated height is greater than or equal to
  188. // the Bitmap Height, this routine will stop. This
  189. // is a non-linear equation.
  190. //
  191. //
  192. // After x is greater than the calculated maxTriangleDistanceFromPE,
  193. // the triangle will turn into a trapezoid for the remainder
  194. // of the page turn animation.
  195. // ________
  196. // + |
  197. // + |
  198. // + |
  199. // + | Bitmap Height. Lies along the page edge PE
  200. // + |
  201. // +a____x________|
  202. // maxTriangleDistanceFromPE
  203. private void calcPathChangeDistAngle(float height, float width, ref double outAngle, ref int distanceFromPE)
  204. {
  205. double radians;
  206. double a;
  207. double tan;
  208. double calculatedHeight;
  209. double previousHeight;
  210. double previous_a = 0;
  211. // Because the Angle a is a function of the distance from the page edge
  212. // and the calculated height is also a function of x;
  213. // the resulting equation is a non linear equation.
  214. // ------- Angle a ----------
  215. // h = x Tan ( 45 + ((45 * (x)) / width) )
  216. //
  217. // One way to solve this problem is using a brute force approach. This
  218. // is not efficient but it solves the problem for
  219. // a limited and bounded value for x.
  220. //
  221. for (int x = 0; x < width; x++)
  222. {
  223. a = 45 + ((45 * (x)) / width);
  224. radians = a * (Math.PI / 180);
  225. tan = Math.Tan(radians);
  226. calculatedHeight = (x) * Math.Tan(radians);
  227. // These will check to see if the distance limit has been reached and also
  228. // save that distance and the corresponding angle.
  229. if (calculatedHeight >= (double)height)
  230. {
  231. distanceFromPE = x;
  232. outAngle = a;
  233. // distanceFromPE = (x - 1);
  234. // outAngle = previous_a;
  235. break;
  236. }
  237. previous_a = a;
  238. previousHeight = calculatedHeight;
  239. }
  240. }
  241. #endregion
  242. // Loads the sample images.
  243. /// <summary>
  244. /// 加载图片
  245. /// </summary>
  246. public void LoadSamples()
  247. {
  248. // Sets up an array of colors. This will give each page a different color.
  249. //颜色数组,给每一页设置不同的颜色
  250. Color[] myColors = new Color[] { Color.Blue,Color.Green,Color.Aquamarine,Color.Red,Color.Yellow,Color.Orange};
  251. bitmaps = new Bitmap[6];
  252. // Prepare the off screen combined bitmap.
  253. //设置后台图片
  254. CurrentShownBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
  255. // The hotSpot is the location of the line of symmetry.
  256. //热点
  257. hotSpot = new Circle(new Point(230, 370), 6);
  258. // Create 6 Bitmaps for this animation
  259. // These fields are used to initialize and bound the movement of the hotspot.
  260. PAGE_SPINE = this.Width / 2; // This should be the middle,书脊位置
  261. PAGE_BOTTOM = this.Height; // Location of the bottom of the pages. Height from the top of the control
  262. BOOK_WIDTH = this.Width; // Edge of the page
  263. PAGE_WIDTH = this.Width / 2;//页面宽度
  264. PAGE_HEIGHT = this.Height - heightOffsetForAnimationTop;//页面高度
  265. // Create a linear gradient brush with alpha for the page Spine
  266. LinearGradientBrush lgb = new LinearGradientBrush((new Rectangle(0, 0, 2, PAGE_HEIGHT)),
  267. (Color.FromArgb(80, 80, 80, 80)), Color.FromArgb(80, Color.White), LinearGradientMode.Horizontal);
  268. for (int i = 1; i <= 6; i++)
  269. {
  270. bitmaps[i - 1] = new Bitmap(this.Width / 2, this.Height - heightOffsetForAnimationTop);
  271. drawPageBitmap(bitmaps[i - 1], (i % 2 == 0), myColors[i - 1], i, INCLUDE_SPIRAL_SPINE, lgb);
  272. }
  273. }
  274. // Used to draw all the pages bitmaps. This is used during initialization and called only by
  275. // LoadSamples
  276. private void drawPageBitmap(Image bitmap,bool isRight,Color c, int number,bool includeSpiral,LinearGradientBrush lgb)
  277. {
  278. Graphics g;
  279. g = Graphics.FromImage(bitmap);
  280. g.SmoothingMode = SmoothingMode.AntiAlias;
  281. g.FillRectangle(new SolidBrush(c), new Rectangle(0, 0, bitmap.Width, bitmap.Height));
  282. g.DrawString(number.ToString(), new Font("Arial", 180), new SolidBrush(Color.Black), new PointF(-10F, 10F));
  283. if (INCLUDE_SPIRAL_SPINE == true)
  284. {
  285. if(isRight)
  286. {
  287. DrawRightPageSpine(g, PAGE_SPINE, bitmap.Width, bitmap.Height);
  288. }
  289. else
  290. {
  291. DrawLeftPageSpine(g, PAGE_SPINE,bitmap.Width, bitmap.Height);
  292. }
  293. }
  294. else // Do not use a Spiral Spine
  295. {
  296. if(isRight)
  297. {
  298. g.FillRectangle(lgb, 0, 0, 3, bitmap.Height);
  299. }
  300. else
  301. {
  302. g.FillRectangle(lgb, bitmap.Width - 3, 0, 2, bitmap.Height);
  303. }
  304. }
  305. g.DrawRectangle(new Pen(Brushes.Black, 2f), new Rectangle(0, 0, bitmap.Width, bitmap.Height));
  306. g.Dispose();
  307. }
  308. protected override void OnLoad(EventArgs e)
  309. {
  310. // Fill in the offscreen bitmap.
  311. if (CurrentShownBitmap != null)
  312. {
  313. this.BackColor = this.Parent.BackColor;
  314. Graphics g = Graphics.FromImage(CurrentShownBitmap);
  315. g.Clear(this.BackColor);
  316. // Show page 3 and page 4. Start in the middle
  317. g.DrawImage(bitmaps[2], new Point(0, heightOffsetForAnimationTop));
  318. g.DrawImage(bitmaps[3], new Point(bitmaps[2].Width, heightOffsetForAnimationTop));
  319. g.Dispose();
  320. }
  321. }
  322. // This routine is used to get the graphics path for the page under the current page and the page on the
  323. // backside of the turning page. It will return either a triangle or trapezoid graphics path.
  324. private GraphicsPath GetPageUnderGraphicsPath(int x,
  325. ref double a,
  326. int height,
  327. int width,
  328. bool isUnderSide,
  329. TurnType type)
  330. {
  331. double radians;
  332. double calculated_x;
  333. double calculated_y=0d;
  334. int undersideOffset = 0;
  335. GraphicsPath gp = new GraphicsPath();
  336. if ( (type == TurnType.RightPageTurn && isUnderSide) || (type==TurnType.LeftPageTurn && !isUnderSide))
  337. {
  338. undersideOffset = width;
  339. }
  340. // This is the angle formed at the bottom of the rectangle just under the line of symmetry
  341. a = 45d + ((45d * x) / width);
  342. radians = a * (Math.PI / 180); // convert to radians for the math function
  343. if (isPathTrapezoid == false)
  344. {
  345. calculated_y = (x) * (Math.Tan(radians));
  346. if (calculated_y > height)
  347. {
  348. isPathTrapezoid = true;
  349. }
  350. }
  351. if (isPathTrapezoid == true)
  352. {
  353. gp.AddLine(new PointF(Math.Abs(width - x - undersideOffset), height), new PointF(width - undersideOffset, height));
  354. gp.AddLine(new PointF(width - undersideOffset, height), new PointF(width - undersideOffset, 0));
  355. calculated_x = height / Math.Tan(radians);
  356. // This adds a line to the top of the trapezoid, this is the distance in the horizontal direction
  357. // that the line of symmetry has traveled.
  358. gp.AddLine(new Point(width - undersideOffset, 0), new PointF(Math.Abs(width - (x - (float)calculated_x) - undersideOffset), 0));
  359. gp.CloseFigure();
  360. }
  361. else
  362. {
  363. // Still a triangle
  364. calculated_y = (x)* (Math.Tan(radians));
  365. gp.AddLine(new PointF(Math.Abs(width - x - undersideOffset), height), new PointF(width - undersideOffset, height));
  366. gp.AddLine(new PointF(width - undersideOffset, height), new PointF(width - undersideOffset, (height - (float)calculated_y)));
  367. gp.CloseFigure();
  368. }
  369. return gp;
  370. }
  371. protected override void OnPaint(PaintEventArgs e)
  372. {
  373. if (CurrentShownBitmap != null)
  374. {
  375. e.Graphics.DrawImage(CurrentShownBitmap, new Point(0, 0));
  376. }
  377. base.OnPaint(e);
  378. }
  379. protected override void OnPaintBackground(PaintEventArgs e)
  380. {
  381. base.OnPaintBackground(e);
  382. }
  383. // This function begins the left page turn animation.
  384. public void animateLeftPageTurn()
  385. {
  386. if (isInAnimation == false && currentLeftPage>1)
  387. {
  388. animationTurnType=TurnType.LeftPageTurn ;
  389. currentXdistanceFromEdge = 0;
  390. isPathTrapezoid = false;
  391. isInAnimation = true;
  392. timer1.Interval = tickSpeed;
  393. timer1.Enabled = true;
  394. animatingToLeftPage = currentLeftPage - 2;
  395. animatingToRightPage = currentRightPage - 2;
  396. NextUnderBitmap = bitmaps[animatingToLeftPage - 1];
  397. UndersideBitmap = bitmaps[animatingToRightPage - 1];
  398. hotSpot.Origin = new Point(0, PAGE_BOTTOM);
  399. }
  400. }
  401. // This function begins the right page turn animation
  402. public void animateRightPageTurn()
  403. {
  404. if (isInAnimation == false && currentLeftPage<5)
  405. {
  406. animationTurnType = TurnType.RightPageTurn;
  407. currentXdistanceFromEdge = 0;
  408. isPathTrapezoid = false;
  409. isInAnimation = true;
  410. timer1.Interval = tickSpeed;
  411. timer1.Enabled = true;
  412. animatingToLeftPage = currentLeftPage + 2;
  413. animatingToRightPage = currentRightPage + 2;
  414. hotSpot.Origin = new Point(BOOK_WIDTH, PAGE_BOTTOM);
  415. NextUnderBitmap = bitmaps[animatingToRightPage-1];
  416. UndersideBitmap = bitmaps[animatingToLeftPage - 1];
  417. }
  418. }
  419. // TBD. This routine will draw a shadow along the line of symmetry
  420. private void DrawShadow(Graphics g, Point start, Point end, int pageWidth)
  421. {
  422. }
  423. // Draw the page spine on a left page. This will put the page spine on the right
  424. // side of the page
  425. private void DrawLeftPageSpine(Graphics g, int pageMiddle, int pageWidth, int pageHeight)
  426. {
  427. int heightOffset = 0;
  428. int numBinders = 0;
  429. numBinders = pageHeight / LeftBinder.Height;
  430. heightOffset = (pageHeight - (numBinders * LeftBinder.Height)) / 2;
  431. g.TranslateTransform(pageMiddle, 0);
  432. for (int i = 0; i < numBinders; i++)
  433. {
  434. g.DrawImage(LeftBinder, new Point(-LeftBinder.Width, heightOffset + (i * LeftBinder.Height)));
  435. }
  436. g.ResetTransform();
  437. }
  438. // Draw the page spine on a right page. This will put the page spine on the left
  439. // side of the page
  440. private void DrawRightPageSpine(Graphics g, int pageMiddle, int pageWidth, int pageHeight)
  441. {
  442. int heightOffset = 0;
  443. int numBinders = 0;
  444. numBinders = pageHeight / LeftBinder.Height;
  445. heightOffset = (pageHeight - (numBinders * LeftBinder.Height)) / 2;
  446. for (int i = 0; i < numBinders; i++)
  447. {
  448. g.DrawImage(RightBinder, new Point(0, heightOffset + (i * LeftBinder.Height)));
  449. }
  450. }
  451. // This is the main routine for the animation.
  452. // When the timer ticks the page turn effect will be
  453. // advanced.
  454. private void timer1_Tick(object sender, EventArgs e)
  455. {
  456. int timeStart = Environment.TickCount;
  457. int timeStop;
  458. double pageUnderAngle;
  459. double pageUndersideRotationAngle;
  460. double radians;
  461. double angle_a=0;
  462. GraphicsPath pageUnderGraphicsPath;
  463. RectangleF undersidePathBounds;
  464. Bitmap pageUndersideImage=null;
  465. Graphics g; // Graphics to the combined image
  466. Graphics undersideG; // Graphics to the underside image, corresponds to pageUndersideImage
  467. PixelOffsetMode p;
  468. Region oldClipRegion;
  469. Matrix oldTransform;
  470. Matrix PathTranslationMatrix;
  471. // Get a graphics context for the combined image that will show the current pages
  472. // and the animation effects
  473. g = Graphics.FromImage(CurrentShownBitmap);
  474. g.Clear(this.BackColor);
  475. // get a new distance from the edge of the page (either left or right). This is the
  476. // distance along the bottom of the page.
  477. currentXdistanceFromEdge += MOVE_X_BY;
  478. if (currentXdistanceFromEdge >= (PAGE_SPINE))
  479. {
  480. timer1.Enabled = false;
  481. isInAnimation = false;
  482. currentLeftPage = animatingToLeftPage;
  483. currentRightPage = animatingToRightPage;
  484. // At this point the animation is done, draw in the new bitmaps fully
  485. g.DrawImage(bitmaps[currentLeftPage - 1], new Point(0, heightOffsetForAnimationTop));
  486. g.DrawImage(bitmaps[currentRightPage - 1], new Point(bitmaps[currentLeftPage - 1].Width, heightOffsetForAnimationTop));
  487. }
  488. else
  489. {
  490. // An animation is in progress, draw in the current bitmaps. This serves as the base images.
  491. // All animiation effects will be drawn on top of this.
  492. g.DrawImage(bitmaps[currentLeftPage - 1], new Point(0, heightOffsetForAnimationTop));
  493. g.DrawImage(bitmaps[currentRightPage - 1], new Point(bitmaps[currentLeftPage - 1].Width, heightOffsetForAnimationTop));
  494. // During rotation the the underside of the page being turned needs to be displayed. To display this
  495. // image a copy of it is needed to control clipping and then eventual rotation along the line of symmetery.
  496. pageUndersideImage = new Bitmap(PAGE_WIDTH,PAGE_HEIGHT);
  497. if (animationTurnType == TurnType.LeftPageTurn)
  498. {
  499. #region LEFTTURN
  500. // Set the current point of the page turn
  501. hotSpot.translateOrigin(MOVE_X_BY, 0);
  502. // Get the new graphics path for the underlying page
  503. pageUnderGraphicsPath = GetPageUnderGraphicsPath(currentXdistanceFromEdge, ref angle_a, PAGE_HEIGHT, PAGE_WIDTH, false, animationTurnType);
  504. pageUndersideRotationAngle = -(180d - (2 * angle_a));
  505. PathTranslationMatrix = new Matrix();
  506. PathTranslationMatrix.Translate((float)0, (float)heightOffsetForAnimationTop);
  507. pageUnderGraphicsPath.Transform(PathTranslationMatrix);
  508. //Save the old clip region
  509. oldClipRegion = g.Clip;
  510. // Set the new clip region to be that of the calcualted path.
  511. g.Clip = new Region(pageUnderGraphicsPath);
  512. g.DrawImage(NextUnderBitmap, new Point(0, heightOffsetForAnimationTop));
  513. g.Clip = oldClipRegion;
  514. if (INCLUDE_DRAW_GRAPHICS_PATH==true)
  515. {
  516. g.DrawPath(new Pen(Brushes.Gold, 5), pageUnderGraphicsPath);
  517. }
  518. PathTranslationMatrix.Dispose();
  519. // Now Draw the graphics for the page underside of the page being rotated.
  520. // In this section the back side of the current page will be drawn
  521. // This path tranformation will reverse the x coordinates.
  522. pageUnderGraphicsPath = GetPageUnderGraphicsPath(currentXdistanceFromEdge,ref angle_a, PAGE_HEIGHT, PAGE_WIDTH, true, animationTurnType);
  523. undersidePathBounds = pageUnderGraphicsPath.GetBounds();
  524. //pageUndersideImage
  525. if (undersidePathBounds.Width > 0)
  526. {
  527. // Get the graphics object for the underside of the image being turned
  528. undersideG = Graphics.FromImage(pageUndersideImage);
  529. if (USE_PIXEL_MODE_OFFSET == true)
  530. {
  531. undersideG.PixelOffsetMode = PixelOffsetMode.Half;
  532. }
  533. // Set the clip region to the underside of the currently being turned image
  534. undersideG.Clip = new Region(pageUnderGraphicsPath);
  535. // Draw the image; note, only the clip region should be shown
  536. undersideG.DrawImage(UndersideBitmap, new Point(0, 0));
  537. // Dispose of the graphics object
  538. undersideG.Dispose();
  539. // Now this image needs to be translated to the hotspot and then rotated.
  540. PathTranslationMatrix = new Matrix(); // setup the translation matrix
  541. if (USE_PIXEL_MODE_OFFSET == true)
  542. {
  543. PathTranslationMatrix.Translate((float)hotSpot.Origin.X, (float)hotSpot.Origin.Y);
  544. }
  545. else
  546. {
  547. PathTranslationMatrix.Translate((float)hotSpot.Origin.X-1, (float)hotSpot.Origin.Y);
  548. }
  549. PathTranslationMatrix.Rotate((float)((pageUndersideRotationAngle)));
  550. oldTransform = g.Transform;
  551. g.Transform = PathTranslationMatrix;
  552. if (INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION == true)
  553. {//
  554. //g.DrawImage(pageUndersideImage, currentXdistanceFromEdge, -PAGE_HEIGHT);
  555. g.DrawImage(pageUndersideImage, hotSpot.Origin.X - PAGE_WIDTH, -PAGE_HEIGHT );//-UndersideBitmap.Height);
  556. }
  557. g.Transform = oldTransform;
  558. }
  559. #endregion
  560. }
  561. else // The right page is turning
  562. {
  563. #region RIGHTTURN
  564. // Set the current point of the page turn
  565. hotSpot.translateOrigin(-MOVE_X_BY, 0);
  566. // Get the new graphics path for the underlying page
  567. pageUnderGraphicsPath = GetPageUnderGraphicsPath(currentXdistanceFromEdge, ref angle_a, PAGE_HEIGHT, PAGE_WIDTH, false, animationTurnType);
  568. pageUndersideRotationAngle = (180d - (2 * angle_a));
  569. PathTranslationMatrix = new Matrix();
  570. PathTranslationMatrix.Translate((float)PAGE_SPINE, (float)heightOffsetForAnimationTop);
  571. pageUnderGraphicsPath.Transform(PathTranslationMatrix);
  572. //Save the old clip region
  573. oldClipRegion = g.Clip;
  574. // Setting the PixelOffsetMode will start drawing -.5f .
  575. // The pixel offset mode is needed on this graphics object
  576. // to draw the page under area.
  577. if (USE_PIXEL_MODE_OFFSET == true)
  578. {
  579. p = g.PixelOffsetMode;
  580. g.PixelOffsetMode = PixelOffsetMode.Half;
  581. }
  582. // Set the new clip region to be that of the calcualted path.
  583. g.Clip = new Region(pageUnderGraphicsPath);
  584. g.DrawImage(NextUnderBitmap, new Point(PAGE_SPINE, heightOffsetForAnimationTop));
  585. g.Clip = oldClipRegion;
  586. if (INCLUDE_DRAW_GRAPHICS_PATH == true)
  587. {
  588. g.DrawPath(new Pen(Brushes.Gold, 5), pageUnderGraphicsPath);
  589. }
  590. if (USE_PIXEL_MODE_OFFSET == true)
  591. {
  592. // Restore the pixel offset mode
  593. g.PixelOffsetMode = p;
  594. }
  595. PathTranslationMatrix.Dispose();
  596. // Now Draw the graphics for the page underside of the page being rotated.
  597. // In this section the back side of the current page will be drawn
  598. // This path tranformation will reverse the x coordinates.
  599. pageUnderGraphicsPath = GetPageUnderGraphicsPath(currentXdistanceFromEdge, ref angle_a, PAGE_HEIGHT, PAGE_WIDTH, true, animationTurnType);
  600. undersidePathBounds = pageUnderGraphicsPath.GetBounds();
  601. if (undersidePathBounds.Width > 0)
  602. {
  603. // Get the graphics object for the underside of the image being turned
  604. undersideG = Graphics.FromImage(pageUndersideImage);
  605. // Set the clip region to the underside of the currently being turned image
  606. undersideG.Clip = new Region(pageUnderGraphicsPath);
  607. // Draw the image; note, only the clip region should be shown
  608. undersideG.DrawImage(UndersideBitmap, new Point(0, 0));
  609. // Dispose of the graphics object
  610. undersideG.Dispose();
  611. // Now this image needs to be translated to the hotspot and then rotated.
  612. PathTranslationMatrix = new Matrix(); // setup the translation matrix
  613. if (USE_PIXEL_MODE_OFFSET == true)
  614. {
  615. PathTranslationMatrix.Translate((float)hotSpot.Origin.X, (float)hotSpot.Origin.Y);
  616. }
  617. else // Simulate a pixel offset by adding 1. This will cause the underside image
  618. // to slightly overlap the page under area currently displayed.
  619. {
  620. PathTranslationMatrix.Translate((float)hotSpot.Origin.X+1, (float)hotSpot.Origin.Y);
  621. }
  622. PathTranslationMatrix.Rotate((float)((pageUndersideRotationAngle)));
  623. oldTransform = g.Transform;
  624. g.Transform=PathTranslationMatrix;
  625. if (INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION == true)
  626. {
  627. g.DrawImage(pageUndersideImage, -(currentXdistanceFromEdge), -(PAGE_HEIGHT));
  628. }
  629. g.Transform = oldTransform;
  630. }
  631. #endregion
  632. }
  633. if (INCLUDE_DRAW_HOTSPOT == true)
  634. {
  635. hotSpot.Draw(g, Color.Snow);
  636. }
  637. timeStop = Environment.TickCount;
  638. int test = timeStop - timeStart;
  639. }
  640. if (pageUndersideImage != null)
  641. {
  642. pageUndersideImage.Dispose();
  643. }
  644. g.Dispose();
  645. Invalidate();
  646. }
  647. }
  648. // Indicates if the Left page or the right
  649. // page is being turned for the current
  650. // animation
  651. enum TurnType
  652. {
  653. LeftPageTurn,
  654. RightPageTurn
  655. }
  656. enum TurnEffect
  657. {
  658. Angled,
  659. Vertical //not implemented
  660. }
  661. // Simple class used to represent the
  662. // location of the line of symmetry
  663. public class Circle
  664. {
  665. public Point origin;
  666. public int radius;
  667. public Circle(Point _origin, int _radius)
  668. {
  669. origin = _origin;
  670. radius = _radius;
  671. //origin = new Point(50, 200);
  672. }
  673. public Point Origin
  674. {
  675. get
  676. {
  677. return origin;
  678. }
  679. set
  680. {
  681. origin = value;
  682. }
  683. }
  684. public void translateOrigin(int X, int Y)
  685. {
  686. origin.X = Origin.X + X;
  687. origin.Y = Origin.Y + Y;
  688. }
  689. public bool hitTest(Point p)
  690. {
  691. if (p.X == origin.X && p.Y == origin.Y)
  692. {
  693. return true;
  694. }
  695. else
  696. {
  697. if (p.X > (origin.X - radius / 2) && p.X < (origin.X + radius / 2) && (p.Y > origin.Y - radius / 2) && (p.Y < origin.Y + radius / 2))
  698. {
  699. return true;
  700. }
  701. }
  702. return false;
  703. }
  704. public void Draw(Graphics g, Color c)
  705. {
  706. SolidBrush b = new SolidBrush(c);
  707. g.DrawEllipse(new Pen(b), origin.X - radius / 2, origin.Y - radius / 2, radius, radius);
  708. }
  709. }
  710. }