/// <summary> /// Creates the tessellation. /// </summary> /// <param name="tess">The tess.</param> /// <returns></returns> public static Bitmap createTessellation(Tessellation tess) { int tessWidth = Math.Abs(tess.getBaseClick().X - tess.getEndClick().X); int tessHeight = Math.Abs(tess.getBaseClick().Y - tess.getEndClick().Y); // Get the patterns to tile and draw Point[][] patterns = tess.getPattern().getPatterns(); // Determine the number of iterations in the x direction and the number of iterations in the y direction // that need to be used in order to cover the entire tessellation area. // 2 was added so that I can draw it overlapping the User Controls draw area so that there is no whitespace near edges. int iterX = (tess.getBaseClick().Y - tess.getEndClick().Y) / tess.getPattern().iWidth + 6; int iterY = (tess.getBaseClick().Y - tess.getEndClick().Y) / tess.getPattern().iHeight + 6; tess.getUnfoldingPath().Clear(); // What divPatWidth and divPatHeight are used for is to determine the area of iterations that this pattern should run through while drawing in order // for the pattern to not end while using your mouse to scroll the map beyond the bounds of the picturebox. // This method to create a dynamic maps such as this is a necessary so that you are not trying to draw things that are not even visible to the container. // Although, there is a bit of an overlap purposely on the left, right and top because many patterns would simply end while scrolling because they are so large jagged. int divPatWidth = (-tess.getOffset().X) / tess.getPattern().iWidth; int divPatHeight = (-tess.getOffset().Y) / tess.getPattern().iHeight; if (divPatWidth > 0) divPatWidth = 0; if (divPatHeight > 0) divPatHeight = 0; // Run through the iterations in both directions (it draws the Y direction for each X first). for (int i = divPatWidth; i < iterX + divPatWidth; i++) { int iMultGetPatWidth = i * tess.getPattern().iWidth; for (int j = divPatHeight; j < iterY + divPatHeight; j++) { int jMultGetPatHeight = j * tess.getPattern().iHeight; for (int k = 0; k < patterns.Count(); k++) { // Draw each pattern Point[] poly = new Point[patterns.ElementAt<Point[]>(k).Count()]; bool isShapeCollided = false; for (int l = 0; l < patterns.ElementAt<Point[]>(k).Count(); l++) { Point temp = new Point(iMultGetPatWidth + patterns[k][l].X, tess.getPictureBox().Height - 5 - jMultGetPatHeight - patterns[k][l].Y); // This is applied every other row because some patterns do not populate the map perfectly straight up. // For example, the equilateral pattern must be applied at a slight horizontal offset otherwise the pattern would not match up as we draw if (j % 2 == 1) temp.X -= tess.getPattern().iOffset; // This is applied at every iteration, we will subtract 3 times the width to not mess up the iOffset and give it some overlap to the left temp.X -= tess.getPattern().iWidth * 3 - tess.getOffset().X; temp.Y -= tess.getOffset().Y; poly[l] = temp; // Algorithm for highlighting the shapes that have a collision int tempMod = (int)MathUtilities.mod(l - 1, patterns.ElementAt<Point[]>(k).Count()); // Use tempMod to find the correct vertex to determine a line for the current wall if (!isShapeCollided) // Check if this polygon has been determined as part of the unfolding { if (l > 0) { // Check the collisions between the vertices of the wall we're examining currently and the vertices of the base and end clicks if (MathUtilities.isValidIntersect((double)poly[tempMod].X, (double)poly[tempMod].Y, (double)poly[l].X, (double)poly[l].Y, (double)tess.getBaseClick().X + tess.getOffset().X, (double)tess.getBaseClick().Y - tess.getOffset().Y, (double)tess.getEndClick().X + tess.getOffset().X, (double)tess.getEndClick().Y - tess.getOffset().Y)) { isShapeCollided = true; } } // Because we didn't check when l is 0, we must come back around after it has been set (on the last iteration) to check that wall we skipped if (l == patterns.ElementAt<Point[]>(k).Count() - 1) { if (MathUtilities.isValidIntersect((double)poly[l].X, (double)poly[l].Y, (double)poly[0].X, (double)poly[0].Y, (double)tess.getBaseClick().X + tess.getOffset().X, (double)tess.getBaseClick().Y - tess.getOffset().Y, (double)tess.getEndClick().X + tess.getOffset().X, (double)tess.getEndClick().Y - tess.getOffset().Y)) { isShapeCollided = true; } } } } if(isShapeCollided) tess.getUnfoldingPath().Add(poly); } } } Bitmap myBitmap = new Bitmap(tessWidth + tess.getPattern().iWidth*2, tessHeight + tess.getPattern().iHeight*2); using (Graphics g = Graphics.FromImage(myBitmap)) { g.SmoothingMode = SmoothingMode.AntiAlias; g.Clear(Color.White); for (int i = 0; i < tess.getUnfoldingPath().Count; i++) { Point[] poly = tess.getUnfoldingPath()[i]; for (int j = 0; j < poly.Length; j++) { poly[j].X -= tess.getBaseClick().X > tess.getEndClick().X ? tess.getEndClick().X : tess.getBaseClick().X; poly[j].Y -= tess.getBaseClick().Y > tess.getEndClick().Y ? tess.getEndClick().Y : tess.getBaseClick().Y; poly[j].X += tess.getPattern().iWidth - tess.getOffset().X; poly[j].Y += tess.getPattern().iHeight + tess.getOffset().Y; } g.DrawPolygon(System.Drawing.Pens.Black, poly); } Point newBaseClick = new Point(); Point newEndClick = new Point(); int xOffset = tess.getBaseClick().X > tess.getEndClick().X ? tess.getEndClick().X : tess.getBaseClick().X; int yOffset = tess.getBaseClick().Y > tess.getEndClick().Y ? tess.getEndClick().Y : tess.getBaseClick().Y; xOffset -= tess.getPattern().iWidth; yOffset -= tess.getPattern().iHeight; newBaseClick.X = tess.getBaseClick().X - xOffset; newBaseClick.Y = tess.getBaseClick().Y - yOffset; newEndClick.X = tess.getEndClick().X - xOffset; newEndClick.Y = tess.getEndClick().Y - yOffset; System.Drawing.Pen myPen = new Pen(System.Drawing.Brushes.DarkBlue, 2); g.DrawLine(System.Drawing.Pens.Blue, newBaseClick, newEndClick); g.Save(); return myBitmap; } }
/// <summary> /// Centralized method to handle the instantiation of all Shapes and Tessellations. /// </summary> /// <param name="shape">Used to determine which shape to instantiate.</param> /// <param name="ratio">Optional parameter that defaults to 0. (Only used for rectangle)</param> /// <returns> /// Returns a Control array of length 2, the first element being the instance of the Shape /// and the second being the instance of the Tessellation. /// </returns> private void instantiateShapes(out Shape instShape, out Tessellation instTess, int shape, double ratio = 0d) { // Checks for the default tabPage that is added as a placeholder, and removes it if a shape is being created. if (tabControl1.TabPages.Contains(tabPage1)) { tabControl1.TabPages.Remove(tabPage1); } Shape tempShape = null; Tessellation tempTess = null; // Each switch case sets the lastTab field, adds the Shape instance to the shapes List, adds the Tessellation instance to the tessellations List, // adds the Shape to the tabControl and sets the ContextMenu of that tab to the context menu field in the Shape base class. switch (shape) { case 0: tempShape = new Equilateral(); EventSource.output("Equilateral Triangle tab created."); tempTess = new EquilateralTess(); tempTess.Name = "Equilateral" + (tempShape.getShapeCount() - 1); break; case 1: tempShape = new IsosTri90(); EventSource.output("90 Isosceles Triangle tab created."); tempTess = new IsosTri90Tess(); tempTess.Name = "IsosTri90" + (tempShape.getShapeCount() - 1); break; case 2: tempShape = new IsosTri120(); EventSource.output("120 Isosceles Triangle tab created."); tempTess = new IsosTri120Tess(); tempTess.Name = "IsosTri120" + (tempShape.getShapeCount() - 1); break; case 3: tempShape = new Tri3060(); EventSource.output("30-60-90 Triangle tab created."); tempTess = new Tri3060Tess(); tempTess.Name = "Tri3060" + (tempShape.getShapeCount() - 1); break; case 4: tempShape = new Hexagon(); EventSource.output("120 Hexagon tab created."); tempTess = new HexagonTess(); tempTess.Name = "Hexagon" + (tempShape.getShapeCount() - 1); break; case 5: tempShape = new Rhombus(); EventSource.output("60-120 Rhombus tab created."); tempTess = new RhombusTess(); tempTess.Name = "Rhombus" + (tempShape.getShapeCount() - 1); break; case 6: tempShape = new Kite(); EventSource.output("60-90-120 Kite tab created."); tempTess = new KiteTess(); tempTess.Name = "Kite" + (tempShape.getShapeCount() - 1); break; case 7: tempShape = new Rect(ratio); EventSource.output("Rectangle tab created."); tempTess = new RectTess(ratio); tempTess.Name = "Rectangle" + (tempShape.getShapeCount() - 1); break; default: break; } // The order for this must remain the same, the Shape and Tessellation instances must be added to the Lists before they are added to the Controls // because it triggers an event for the tabControl when a new Control is added, and the lastTab must be configured correctly for it. if (shapes.Count() != 0) lastTab = (Shape)tabControl1.SelectedTab; shapes.Add(tempShape); tessellations.Add(tempTess); tabControl1.TabPages.Add(tempShape); instShape = tempShape; instTess = tempTess; }
/// <summary> /// Triggers the methods subscribed to the tessellate event /// </summary> /// <param name="newTess"></param> public static void updateTess(Tessellation newTess) { tessellate(newTess, new Events("Tessellation parameters have been changed.")); }