I have a problem. In my MainActivity I call fragments when you click a button. One of the fragments is a SkiaSharp canvas with a TriangleGrid. When you click on a Triangle, it changes the color of that Triangle. Now when I go the SkiaSharp fragment the first time, everything loads correctly like I want, but when I go to a different page and then go back to the SkiaSharp canvas, The whole canvas is blank. The triangle still get's filled when you tap it, but the TriangleGrid is gone. Also the Triangles that were previously clicked, except for the last one. Here are the most important parts of my code. This is the FULL MainActivity:
public class MainActivity : FragmentActivity
{
Dictionary<string, TriangleRegistryObject> TriangleRegistry = new Dictionary<string, TriangleRegistryObject>();
//ALL FRAGMENTS
private Android.Support.V4.App.Fragment CurrentFragment;
private int CurrentFragmentNum;
private int PreviousFragmentNum;
private Shape_Selection ShapeSelectionFragment;
private Shape_Formation ShapeFormationFragment;
private string Direction;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
ActionBar.SetCustomView(Resource.Layout.setup_nav_bar);
ActionBar.SetDisplayShowCustomEnabled(true);
((Toolbar)ActionBar.CustomView.Parent).SetContentInsetsAbsolute(0, 0);
SetContentView(Resource.Layout.activity_main);
Button btnBack = FindViewById<Button>(Resource.Id.btnBack);
Button btnNext = FindViewById<Button>(Resource.Id.btnNext);
btnBack.Click += btnBack_Click;
btnNext.Click += btnNext_Click;
ShapeSelectionFragment = new Shape_Selection();
ShapeFormationFragment = new Shape_Formation();
var trans = SupportFragmentManager.BeginTransaction();
trans.Add(Resource.Id.fragmentContainer, ShapeFormationFragment, "Shape_Formation");
trans.Hide(ShapeFormationFragment);
trans.Add(Resource.Id.fragmentContainer, ShapeSelectionFragment, "Shape_Selection");
//trans.Hide(ShapeSelectionFragment);
trans.Commit();
CurrentFragmentNum = 1;
Direction = "Startup";
ShowFragment(CurrentFragmentNum, 1, Direction);
}
private void ShowFragment (int CurrentFragmentNum, int PreviousFragmentNum, string Direction)
{
Android.Support.V4.App.Fragment fragment = null;
Android.Support.V4.App.Fragment PreviousFragment = null;
switch (CurrentFragmentNum)
{
case 1:
fragment = ShapeSelectionFragment;
break;
case 2:
fragment = ShapeFormationFragment;
break;
}
switch (PreviousFragmentNum)
{
case 1:
PreviousFragment = ShapeSelectionFragment;
break;
case 2:
PreviousFragment = ShapeFormationFragment;
break;
}
var trans = SupportFragmentManager.BeginTransaction();
switch (Direction)
{
case "Forwards":
trans.SetCustomAnimations(Resource.Animation.slide_in_right, Resource.Animation.slide_out_left);
break;
case "Backwards":
trans.SetCustomAnimations(Resource.Animation.slide_in_left, Resource.Animation.slide_out_right);
break;
}
trans.Replace(Resource.Id.fragmentContainer, fragment);
trans.Hide(PreviousFragment);
trans.Show(fragment);
trans.AddToBackStack(null);
trans.Commit();
CurrentFragment = fragment;
if(CurrentFragment == ShapeSelectionFragment)
{
CurrentFragmentNum = 1;
}
else if (CurrentFragment == ShapeFormationFragment)
{
CurrentFragmentNum = 2;
}
}
public void btnBack_Click(object sender, EventArgs e)
{
if (CurrentFragmentNum > 1)
{
PreviousFragmentNum = CurrentFragmentNum;
CurrentFragmentNum -= 1;
Direction = "Backwards";
ShowFragment(CurrentFragmentNum, PreviousFragmentNum, Direction);
}
}
public void btnNext_Click(object sender, EventArgs e)
{
if (CurrentFragmentNum < 2)
{
PreviousFragmentNum = CurrentFragmentNum;
CurrentFragmentNum += 1;
Direction = "Forwards";
ShowFragment(CurrentFragmentNum, PreviousFragmentNum, Direction);
}
}
}
And this is FULL the Shape_Formation.cs:
public class Shape_Formation : Android.Support.V4.App.Fragment, IOnTouchListener
{
private SKCanvasView skiaView;
private SKPaint TriangleFillColor;
private string Action { get; set; }
Dictionary<string, TriangleRegistryObject> TriangleRegistry = new Dictionary<string, TriangleRegistryObject>();
private int UnscaledWidth;
private int UnscaledHeight;
private int TrianglesPerUpperRow;
private int TrianglesPerLowerRow;
private int TrianglesVisiblePerRow = 4;
private int CurrentRow = 0;
private float TriangleWidth;
private float TriangleRowHeight;
//ALL SHAPE PATHS
private SKPath TriangleGridPath = new SKPath();
private SKPath TrianglePath = new SKPath();
//================//
// PAINT VARIANTS //
//================//
SKPaint TriangleGridPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 3
};
SKPaint SelectTrianglePaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue
};
SKPaint UnselectTrianglePaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColor.Parse("#0e2a56")
};
SKPaint BackgroundPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColor.Parse("#0e2a56")
};
SKPaint FocusedTrianglePaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.LightGray
};
//=======================//
// END OF PAINT VARIANTS //
//=======================//
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.setup_shape_formation, container, false);
skiaView = view.FindViewById<SKCanvasView>(Resource.Id.skiaView);
skiaView.SetOnTouchListener(this);
skiaView.PaintSurface += PaintSurface;
return view;
}
private void PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
//==============//
// CANVAS SETUP //
//==============//
SKCanvas canvas = e.Surface.Canvas;
//SOME VARIABLES OF SCREEN SIZE
UnscaledWidth = e.Info.Width;
UnscaledHeight = e.Info.Height;
//=====================//
// END OF CANVAS SETUP //
//=====================//
if (TriangleFillColor != null)
{
canvas.DrawPath(TrianglePath, TriangleFillColor);
canvas.DrawPath(TrianglePath, TriangleGridPaint);
}
//====================//
// DRAW TRIANGLE GRID //
//====================//
int TriangleCount = 0;
TriangleWidth = UnscaledWidth / TrianglesVisiblePerRow;
TriangleRowHeight = (float)Math.Sqrt(Math.Pow(TriangleWidth, 2) - (Math.Pow((UnscaledWidth / (TrianglesVisiblePerRow * 2)), 2)));
CurrentRow = 1;
float X_Start_CooridnateUpperRow = 0;
float Y_Start_CooridnateUpperRow = 0;
float X_Start_CooridnateLowerRow = -(TriangleWidth / 2);
float Y_Start_CooridnateLowerRow = TriangleRowHeight;
SKPaint TriangleBorderPaint;
//DRAW MULTIPLE ROWS
while (Y_Start_CooridnateUpperRow <= UnscaledHeight)
{
TrianglesPerUpperRow = 5;
TrianglesPerLowerRow = 5;
//SET THE TRIANGLE COUNTER FOR BOTH ROWS
int TriangleUpperCountOnRow = 0;
int TriangleLowerCountOnRow = 0;
//DRAW THE UPPER ROW OF TRIANGLES
while (TriangleUpperCountOnRow < TrianglesPerUpperRow)
{
float x1 = X_Start_CooridnateUpperRow;
float y1 = Y_Start_CooridnateUpperRow;
float x2 = X_Start_CooridnateUpperRow + TriangleWidth;
float y2 = y1;
float x3 = X_Start_CooridnateUpperRow + TriangleWidth / 2;
float y3 = TriangleRowHeight * (CurrentRow);
TriangleGridPath = new SKPath();
TriangleGridPath.MoveTo(x1, y1);
TriangleGridPath.LineTo(x2, y2);
TriangleGridPath.LineTo(x3, y3);
TriangleGridPath.LineTo(x1, y1);
TriangleGridPath.Close();
TriangleRegistryObject value;
TriangleRegistry.TryGetValue(TriangleCount.ToString(), out value);
if (value == null)
{
if (x1 > UnscaledWidth ||
x2 > UnscaledWidth ||
x3 > UnscaledWidth ||
x1 < 0 ||
x2 < 0 ||
x3 < 0 ||
y1 > UnscaledHeight ||
y2 > UnscaledHeight ||
y3 > UnscaledHeight)
{
TriangleBorderPaint = BackgroundPaint;
TriangleRegistry[TriangleCount.ToString()] = new TriangleRegistryObject { x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3, Selected = false, Visible = false };
}
else
{
TriangleBorderPaint = TriangleGridPaint;
TriangleRegistry[TriangleCount.ToString()] = new TriangleRegistryObject { x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3, Selected = false, Visible = true };
}
canvas.DrawPath(TriangleGridPath, TriangleBorderPaint);
}
TriangleCount += 1;
TriangleUpperCountOnRow += 1;
X_Start_CooridnateUpperRow += TriangleWidth;
}
//DRAW THE LOWER ROW OF TRIANGLES
while (TriangleLowerCountOnRow < TrianglesPerLowerRow)
{
float x1 = X_Start_CooridnateLowerRow;
float y1 = Y_Start_CooridnateLowerRow;
float x2 = X_Start_CooridnateLowerRow + TriangleWidth;
float y2 = y1;
float x3 = X_Start_CooridnateLowerRow + (TriangleWidth / 2);
float y3 = TriangleRowHeight * (CurrentRow - 1);
TriangleGridPath = new SKPath();
TriangleGridPath.MoveTo(x1, y1);
TriangleGridPath.LineTo(x2, y2);
TriangleGridPath.LineTo(x3, y3);
TriangleGridPath.LineTo(x1, y1);
TriangleGridPath.Close();
TriangleRegistryObject value;
TriangleRegistry.TryGetValue(TriangleCount.ToString(), out value);
if (value == null)
{
if (x1 > UnscaledWidth ||
x2 > UnscaledWidth ||
x3 > UnscaledWidth ||
x1 < 0 ||
x2 < 0 ||
x3 < 0 ||
y1 > UnscaledHeight ||
y2 > UnscaledHeight ||
y3 > UnscaledHeight)
{
TriangleBorderPaint = BackgroundPaint;
TriangleRegistry[TriangleCount.ToString()] = new TriangleRegistryObject { x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3, Selected = false, Focused = false, Visible = false };
}
else
{
TriangleBorderPaint = TriangleGridPaint;
TriangleRegistry[TriangleCount.ToString()] = new TriangleRegistryObject { x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3, Selected = false, Focused = false, Visible = true };
}
canvas.DrawPath(TriangleGridPath, TriangleBorderPaint);
}
TriangleCount += 1;
TriangleLowerCountOnRow += 1;
X_Start_CooridnateLowerRow += TriangleWidth;
}
CurrentRow += 1;
if ((CurrentRow - 1) % 2 != 0)
{
X_Start_CooridnateUpperRow = -(TriangleWidth / 2);
X_Start_CooridnateLowerRow = 0;
}
else
{
X_Start_CooridnateUpperRow = 0;
X_Start_CooridnateLowerRow = -(TriangleWidth / 2);
}
Y_Start_CooridnateUpperRow += TriangleRowHeight;
Y_Start_CooridnateLowerRow += TriangleRowHeight;
}
//==============================//
// END OF DRAWING TRIANGLE GRID //
//==============================//
}
public override void OnDestroy()
{
skiaView.PaintSurface -= PaintSurface;
base.OnDestroy();
}
private void DrawTrianglePath(double x1, double y1, double x2, double y2, double x3, double y3, string key, string Action)
{
bool ChangedState = true;
var Triangle = TriangleRegistry[key];
if (Triangle.Visible == true)
{
if (Triangle.Selected == false)
{
if (Action == "Selected")
{
Triangle.Selected = true;
TriangleRegistry[key] = Triangle;
TriangleFillColor = SelectTrianglePaint;
}
else if (Action == "Focused")
{
if (Triangle.Focused == false)
{
Triangle.Focused = true;
TriangleRegistry[key] = Triangle;
TriangleFillColor = FocusedTrianglePaint;
}
}
}
else
{
Triangle.Selected = false;
TriangleFillColor = UnselectTrianglePaint;
}
if (ChangedState == true)
{
TrianglePath = new SKPath();
TrianglePath.MoveTo((float)Triangle.x1, (float)Triangle.y1);
TrianglePath.LineTo((float)Triangle.x2, (float)Triangle.y2);
TrianglePath.LineTo((float)Triangle.x3, (float)Triangle.y3);
TrianglePath.LineTo((float)Triangle.x1, (float)Triangle.y1);
TrianglePath.Close();
skiaView.Invalidate();
}
}
}
public bool OnTouch(View v, MotionEvent e)
{
if (e.Action == MotionEventActions.Down)
{
var X_Clicked = e.GetX();
var Y_Clicked = e.GetY();
bool TriangleFound = false;
while (TriangleFound == false)
{
foreach (KeyValuePair<string, TriangleRegistryObject> row in TriangleRegistry.ToList())
{
if (isInside(row.Value.x1, row.Value.y1, row.Value.x2, row.Value.y2, row.Value.x3, row.Value.y3, X_Clicked, Y_Clicked))
{
TriangleFound = true;
Action = "Selected";
DrawTrianglePath(row.Value.x1, row.Value.y1, row.Value.x2, row.Value.y2, row.Value.x3, row.Value.y3, row.Key, Action);
}
}
}
}
return true;
}
static double area(double x1, double y1, double x2, double y2, double x3, double y3)
{
return Math.Abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0);
}
static bool isInside(double x1, double y1, double x2, double y2, double x3, double y3, double x, double y)
{
double A = area(x1, y1, x2, y2, x3, y3);
double A1 = area(x, y, x2, y2, x3, y3);
double A2 = area(x1, y1, x, y, x3, y3);
/* Calculate area of triangle PAB */
double A3 = area(x1, y1, x2, y2, x, y);
/* Check if sum of A1, A2 and A3 is same as A */
return (A == A1 + A2 + A3);
}
}
Can someone help me with this problem!?
you could see these lines of code in your ShowFragment:
trans.Replace(Resource.Id.fragmentContainer, fragment);
trans.Hide(PreviousFragment);
trans.Show(fragment);
trans.AddToBackStack(null);
trans.Commit();
you use replace,it will destory the fragment you added it in Resource.Id.fragmentContainer before,so it couldn't store the canvas,here you only want to show and hide the fragment you added before,you could just change like this(This will not destroy the fragment but hide and show):
trans.Hide(PreviousFragment);
trans.Show(fragment);
trans.Commit();
Related
About
I’m using WinForms. In my form, I have a picturebox. The picturebox size mode is set to zoom. I use the picturebox to view TIF images. The TIF images are grayscale (Black and White ONLY).
What My App Does
My application finds the first black pixel and the last black pixel in the document and draws a red rectangle around it. In hopes that it would draw the rectangle around the content of the image.
The Problem
Sometimes the TIF documents have spots/dots around the content of the image. This throws my application off. It doesn't know where the content of the image begins and ends. How can I find the content of the TIF documents and draw a rectangle around it if the document has spots/dots?
About the Document
Black and white only (1 bit depth)
TIF document
Document always has letters and numbers
The letters and numbers are always bigger than the spots/dots
There can be multiple spots all over the image even in the content
The background is always white
The content is always black
The spots are also black
Download Test Image Links:
• File Dropper: http://www.filedropper.com/test-tifs
• Rapid Share: https://ufile.io/2qiir
What I Found
Upon my research, I found AForge.Imaging library which has many imaging filters that may potentially help me achieve my goal. I'm thinking about removing the spots/dots using the median filter or use the other filters to achieve the desired result.
What I Tried
I tried applying the median filter from AForge library to get rid of the spots but that only got rid of some of the spots. I had to repeat replying the filter multiple times to get rid of MOST of the spots to find the content and it still had a hard time finding the content. That method didn't work too well for me.
Link to AForge Filters: http://www.aforgenet.com/framework/docs/html/cdf93487-0659-e371-fed9-3b216efb6954.htm
Code
private void btn_Draw_Click(object sender, EventArgs e)
{
// Wrap the creation of the OpenFileDialog instance in a using statement,
// rather than manually calling the Dispose method to ensure proper disposal
using (OpenFileDialog dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(dlg.FileName);
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
startX = Int32.MaxValue;
startY = Int32.MaxValue;
endX = Int32.MinValue;
endY = Int32.MinValue;
using (Bitmap bmp = new Bitmap(pictureBox1.Image))
{
for (var y = 0; y < yMax; y+=3)
{
for (var x = 0; x < xMax; x+=3)
{
Color col = bmp.GetPixel(x, y);
if(col.ToArgb() == Color.Black.ToArgb())
{
// Finds first black pixel
if (x < startX)
startX = x;
if(y < startY)
startY = y;
// Finds last black pixel
if (x > endX)
endX = x;
if (y > endY)
endY = y;
}
}
}
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//var scaleX = picWidth / (float)xMax;
//var scaleY = picHeight / (float)yMax;
//startX = (int)Math.Round(startX * scaleX);
//startY = (int)Math.Round(startY * scaleY);
//endX = (int)Math.Round(endX * scaleX);
//endY = (int)Math.Round(endY * scaleY);
}
}
}
}
private bool _once = true;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_once)
{
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
Tif document that DOES NOT HAVE any spots and dots around content
Tif document that HAS spots and dots around content
Example Image 1:
Example Image 2
:
Example Image 3
Following experiment seems to meet all your requirements.
I put following controls onto Form1
A MenuStrip: Docking=Top, with 2 MenuItems - one to open a file, second to run an algorithm
A progressbar: Docking=Top, to watch performance of loading and algorithm
A panel with Docking=Fill and AutoScroll=true
A picture into the panel, Point(0,0), the rest by default. SizeMode=Normal.
Update
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Opens an image file.
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
this.image = Image.FromFile(dlg.FileName) as Bitmap;
this.pictureBox1.Image = image;
this.pictureBox1.Invalidate();
}
}
Bitmap image;
// finds top, left, right and bottom bounds of the content in TIFF file.
//
private void findBoundsToolStripMenuItem_Click(object sender, EventArgs e)
{
int contentSize = 70;
this.left = 0;
this.top = 0;
this.right = this.pictureBox1.Width - 1;
this.bottom = this.pictureBox1.Height - 1;
int h = image.Height;
int w = image.Width;
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 4;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
int size = this.image.GetBlackRegionSize(x, y);
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.top = y;
goto label10;
}
}
}
}
label10:
this.progressBar1.Increment(1);
for (int y = h - 1; y >= 0; y--)
{
for (int x = 0; x < w; x++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.bottom = y;
goto label11;
}
}
}
}
label11:
this.progressBar1.Increment(1);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.left = x;
goto label12;
}
}
}
}
label12:
this.progressBar1.Increment(1);
for (int x = w - 1; x >= 0; x--)
{
for (int y = 0; y < h; y++)
{
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
if (this.image.GetBlackRegionSize(x, y) > contentSize)
{
this.right = x;
goto label13;
}
}
}
}
label13:
this.progressBar1.Increment(1);
this.pictureBox1.Invalidate();
}
internal static readonly int Black = Color.Black.ToArgb();
internal static readonly int White = Color.White.ToArgb();
int top;
int bottom;
int left;
int right;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (pictureBox1.Image == null)
{
return;
}
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
int startX = this.left;
int startY = this.top;
int endX = this.right;
int endY = this.bottom;
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//if (_once)
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
static class BitmapHelper
{
internal static int GetBlackRegionSize(this Bitmap image, int x, int y)
{
int size = 0;
GetRegionSize(image, new List<Point>(), x, y, 0, ref size);
return size;
}
// this constant prevents StackOverFlow exception.
// also it has effect on performance.
// It's value must be greater than the value of contentSize defined in findBoundsToolStripMenuItem_Click(object sender, EventArgs e) method.
const int MAXLEVEL = 100;
static void GetRegionSize(this Bitmap image, List<Point> list, int x, int y, int level, ref int size)
{
if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || list.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black || level > MAXLEVEL)
{
return;
}
if (size < level)
{
size = level;
}
list.Add(new Point(x, y));
image.GetRegionSize(list, x, y - 1, level + 1, ref size);
image.GetRegionSize(list, x, y + 1, level + 1, ref size);
image.GetRegionSize(list, x - 1, y, level + 1, ref size);
image.GetRegionSize(list, x + 1, y, level + 1, ref size);
}
static bool Contains(this List<Point> list, int x, int y)
{
foreach (Point point in list)
{
if (point.X == x && point.Y == y)
{
return true;
}
}
return false;
}
}
}
"this.pictureBox1.Size = image.Size;" has been removed. Paint event handler's code changed. PictureBox size mode can be set to Zoom now.
Update 2
I tried to simplify code and increase performance.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.pictureBox1.Paint += new PaintEventHandler(this.pictureBox1_Paint);
}
// Opens an image file
// and runs "FindBounds()" method
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
this.image = Image.FromFile(dlg.FileName) as Bitmap;
FindBounds();
this.pictureBox1.Image = image;
this.pictureBox1.Invalidate();
}
}
Bitmap image;
// Possible maximum side of a spot or a dot in the image
int maxSpotOrDotSide = 7;
// Finds top, left, right and bottom bounds of the content in TIFF file.
private void FindBounds()
{
// Possible maximum area of a spot or a dot in the image
int maxSpotOrDotArea = maxSpotOrDotSide * maxSpotOrDotSide;
this.left = 0;
this.top = 0;
this.right = this.pictureBox1.Width - 1;
this.bottom = this.pictureBox1.Height - 1;
int h = image.Height;
int w = image.Width;
int num = w * h;
// Incrementers. I tested with greater values
// like "x = 2", "x = 5" and it increased performance.
// But we must be carefull as this may cause skipping content.
int dx = 1; // Incrementer for "x"
int dy = 1; // Incrementer for "y"
// Initialization of "progressBar1"
this.progressBar1.Value = 0;
this.progressBar1.Maximum = num;
// Content of the image
BlackContent imageContent = null;
// Here we will scan pixels of the image
// starting from top left corner and
// finishing at bottom right
for (int y = 0; y < h; y += dx)
{
for (int x = 0; x < w; x += dy)
{
int val = y * w + x;
this.progressBar1.Value = val > num ? num : val;
// This block skips scanning imageContent
// thus should increase performance.
if (imageContent != null && imageContent.Contains(x, y))
{
x = imageContent.Right;
continue;
}
// Interesting things begin to happen
// after we detect the first Black pixel
if (this.image.GetPixel(x, y).ToArgb() == Black)
{
BlackContent content = new BlackContent(x, y);
// Start Flood-Fill algorithm
content.FloodFill(this.image);
if (content.Area > maxSpotOrDotArea)
{
if (imageContent == null)
{
imageContent = content;
}
imageContent.Include(content.Right, content.Bottom);
imageContent.Include(content.Left, content.Top);
}
else
{
// Here it's better we increase values of the incrementers.
// Depending on size of spots/dots.
// It should increase performance.
if (dx < content.Width) dx = content.Width;
if (dy < content.Height) dy = content.Height;
}
}
}
}
// Everything is done.
this.progressBar1.Value = this.progressBar1.Maximum;
// If image content has been detected
// then we save the information
if (imageContent != null)
{
this.left = imageContent.Left;
this.top = imageContent.Top;
this.right = imageContent.Right;
this.bottom = imageContent.Bottom;
}
this.pictureBox1.Invalidate();
}
internal static readonly int Black = Color.Black.ToArgb();
internal static readonly int White = Color.White.ToArgb();
int top;
int bottom;
int left;
int right;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (pictureBox1.Image == null)
{
return;
}
int xMax = pictureBox1.Image.Width;
int yMax = pictureBox1.Image.Height;
int startX = this.left;
int startY = this.top;
int endX = this.right;
int endY = this.bottom;
int picWidth = pictureBox1.Size.Width;
int picHeight = pictureBox1.Size.Height;
float imageRatio = xMax / (float)yMax; // image W:H ratio
float containerRatio = picWidth / (float)picHeight; // container W:H ratio
if (imageRatio >= containerRatio)
{
// horizontal image
float scaleFactor = picWidth / (float)xMax;
float scaledHeight = yMax * scaleFactor;
// calculate gap between top of container and top of image
float filler = Math.Abs(picHeight - scaledHeight) / 2;
//float filler = 0;
startX = (int)(startX * scaleFactor);
endX = (int)(endX * scaleFactor);
startY = (int)((startY) * scaleFactor + filler);
endY = (int)((endY) * scaleFactor + filler);
}
else
{
// vertical image
float scaleFactor = picHeight / (float)yMax;
float scaledWidth = xMax * scaleFactor;
float filler = Math.Abs(picWidth - scaledWidth) / 2;
startX = (int)((startX) * scaleFactor + filler);
endX = (int)((endX) * scaleFactor + filler);
startY = (int)(startY * scaleFactor);
endY = (int)(endY * scaleFactor);
}
//if (_once)
//Rectangle ee = new Rectangle(35, 183, 405, 157);
Rectangle ee = new Rectangle(startX, startY, endX - startX, endY - startY);
System.Diagnostics.Debug.WriteLine(startX + ", " + startY + ", " + (endX - startX) + ", " + (endY - startY));
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, ee);
}
//_once = false;
}
}
// This class is similar to System.Drawing.Region class
// except that its only rectangular.
// Because all we need is to draw a rectagnle
// around the image this property must
// make it faster, at least I hope so.
class BlackContent
{
internal void FloodFill(Bitmap image)
{
FloodFillPrivate(image, this.left + 1, this.top, 0);
}
// Legendary Flood-Fill algorithm.
// Quite often it ends up with StackOverFlow exception.
// But this class and its rectangularity property
// must prevent this disaster.
// In my experiments I didn't encounter incidents.
void FloodFillPrivate(Bitmap image, int x, int y, int level)
{
if (x >= image.Width || x < 0 || y >= image.Height || y < 0 || this.Contains(x, y) || image.GetPixel(x, y).ToArgb() != Form1.Black)
{
return;
}
this.Include(x, y);
FloodFillPrivate(image, x, y - 1, level + 1);
FloodFillPrivate(image, x, y + 1, level + 1);
FloodFillPrivate(image, x - 1, y, level + 1);
FloodFillPrivate(image, x + 1, y, level + 1);
}
internal BlackContent(int x, int y)
{
this.left = x;
this.right = x;
this.top = y;
this.bottom = y;
}
internal void Include(int x, int y)
{
if (x < this.left)
{
this.left = x;
}
if (this.right < x)
{
this.right = x;
}
if (this.bottom < y)
{
this.bottom = y;
}
if (y < this.top)
{
this.top = y;
}
}
internal bool Contains(int x, int y)
{
return !(x < this.left || x > this.right || y < this.top || y > this.bottom);
}
int left;
internal int Left { get { return this.left; } }
int top;
internal int Top { get { return this.top; } }
int right;
internal int Right { get { return this.right; } }
int bottom;
internal int Bottom { get { return this.bottom; } }
internal int Area
{
get
{
return Width * Height;
}
}
internal int Width
{
get
{
return (this.right - this.left + 1);
}
}
internal int Height
{
get
{
return (this.bottom - this.top + 1);
}
}
}
}
I watched the performance with ProgressBar. This one's quite faster.
I must also mention that your images are too big.
A solution could be to find areas of black pixels. When a black pixel is found, check the colour of the neighbouring pixels and create an area of black pixels. When the area is big enough, it can be considered as content. The pseudo code below illustrates this. But it is a very resource intensive solution and should at least be optimized.
private List<List<Point>> areas = new List<List<Point>>();
public void PopulateAreas()
{
//Find all the areas
for (var y = 0; y < yMax; y += 3)
{
for (var x = 0; x < xMax; x += 3)
{
Color col = bmp.GetPixel(x, y);
if (col.ToArgb() == Color.Black.ToArgb())
{
//Found a black pixel, check surrounding area
var area = new List<Point>();
area.Add(new Point(x, y));
areas.Add(area);
AppendSurroundingPixelsToArea(area, x, y);
}
}
}
var startX = Int32.MaxValue;
var startY = Int32.MaxValue;
var endX = Int32.MinValue;
var endY = Int32.MinValue;
//Loop through list of areas.
foreach (var area in areas)
{
//Minimum size of area
if (area.Count > 5)
{
var minx = area.Min(p => p.X);
if (area.Min(p => p.X) < startX)
startX = minx;
//Do the same for the others...
}
}
}
public void AppendSurroundingPixelsToArea(List<Point> area, int startX, int startY)
{
for(var x = startX - 1; x <= startX + 1; x++)
for (var y = startY - 1; y <= startY + 1; y++)
{
if ((x != 0 || y != 0) && IsBlackPixel(bmp, x, y))
{
//Add to the area
if (PointDoesNotExistInArea(area, x, y))
{
area.Add(new Point(x, y));
AppendSurroundingPixelsToArea(area, x, y);
}
}
}
}
I have solution for this,
And I suggest "kodak imaging professional". This is the viewer to display multi-page tiff files. with many features like: Annotation, invert color, rotate image... etc., and these are inbuilt functionalities.
I am making an automated floor plan generation desktop app. In this, at first I draw the polygon on points, using this method
public void DrawPolygonPointF(PaintEventArgs e) {
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create points that define polygon.
PointF point1 = new PointF(50.0F, 50.0F);
PointF point2 = new PointF(100.0F, 25.0F);
PointF point3 = new PointF(200.0F, 5.0F);
PointF point4 = new PointF(250.0F, 50.0F);
PointF point5 = new PointF(300.0F, 100.0F);
PointF point6 = new PointF(350.0F, 200.0F);
PointF point7 = new PointF(250.0F, 250.0F);
PointF[] curvePoints =
{
point1,
point2,
point3,
point4,
point5,
point6,
point7
};
// Draw polygon curve to screen.
e.Graphics.DrawPolygon(blackPen, curvePoints);
}
NOTE: These points are not actual points, they are for only demo purpose. I am reading the points from a text file.
Now I need to generate a special type of grid.
In generating the grid the first step is detect corners and extend the corner lines.
How do I detect the corners of a polygon so can i move to next step of generating grid?
Corners are marked. I need to extend corner marked with black on horizontally left side and other one is to extend on right side till its touch the line.
A screenshot is attached.
Thanks in advance
In my understanding you are trying to extend edges, not corners.
the procedure could be:
Enumerate edges (each 2 adjacent points define an edge)
For each edge find if it vertical or horizontal (abs(x1-x2) > abs(y1-y2))
Find if edge can be extended, the horizontal (left/right) and the vertical (up/bottom)
bool CheckHorizontalExtensibilityToRight(Point[] curvePoints, Point corner)
{
return curvePoints.Any(cp=>cp.Y < corner.Y && cp.X < corner.X);
}
Try this example and see if you can adapt it to solve your problem...
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
public partial class Form1 : Form
{
private struct LineSegment
{
private PointF _a, _b;
public PointF A { get { return _a; } }
public PointF B { get { return _b; } }
public LineSegment(PointF a, PointF b)
{
_a = a; _b = b;
}
public float GetLengthSquared()
{
var dx = _a.X - _b.X;
var dy = _a.Y - _b.Y;
return dx * dx + dy * dy;
}
public bool RectContains(PointF a)
{
var x = a.X;
var y = a.Y;
var x1 = _a.X;
var y1 = _a.Y;
var x2 = _b.X;
var y2 = _b.Y;
return (x1 < x2 ? x1 <= x && x2 >= x : x2 <= x && x1 >= x) && (y1 < y2 ? y1 <= y && y2 >= y : y2 <= y && y1 >= y);
}
public bool ExtendToIntersectWith(LineSegment b)
{
var x1 = _a.X;
var y1 = _a.Y;
var x2 = _b.X;
var y2 = _b.Y;
var x3 = b._a.X;
var y3 = b._a.Y;
var x4 = b._b.X;
var y4 = b._b.Y;
var a1 = y2 - y1;
var b1 = x1 - x2;
var c1 = x1 * y2 - x2 * y1;
var a2 = y4 - y3;
var b2 = x3 - x4;
var c2 = x3 * y4 - x4 * y3;
var d = a1 * b2 - b1 * a2;
if (d == 0)
return false;
var x = (c1 * b2 - b1 * c2) / d;
var y = (a1 * c2 - c1 * a2) / d;
var p = new PointF(x, y);
if (b.RectContains(p) && !RectContains(p))
{
if (new LineSegment(_a, p).GetLengthSquared() < new LineSegment(_b, p).GetLengthSquared())
_a = p;
else
_b = p;
return true;
}
return false;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
PointF[] curvePoints =
{
/*
new PointF(50.0F, 50.0F),
new PointF(100.0F, 25.0F),
new PointF(200.0F, 5.0F),
new PointF(250.0F, 50.0F),
new PointF(300.0F, 100.0F),
new PointF(350.0F, 200.0F),
new PointF(250.0F, 250.0F)
*/
new PointF(30F, 10F),
new PointF(60F, 10F),
new PointF(60F, 20F),
new PointF(90F, 20F),
new PointF(90F, 60F),
new PointF(10F, 60F),
new PointF(10F, 40F),
new PointF(30F, 40F),
};
int n = curvePoints.Length;
LineSegment[] lineSegments = new LineSegment[n];
int i = 0;
for (; i < n - 1; ++i)
lineSegments[i] = new LineSegment(curvePoints[i], curvePoints[i + 1]);
lineSegments[i] = new LineSegment(curvePoints[i], curvePoints[0]);
for (i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
lineSegments[i].ExtendToIntersectWith(lineSegments[j]);
for (i = 0; i < n; ++i)
{
var lineSegment = lineSegments[i];
e.Graphics.DrawLine(Pens.Black, lineSegment.A, lineSegment.B);
}
//e.Graphics.DrawPolygon(Pens.Black, curvePoints);
}
}
}
I am working on a project where I have to align series of images so for that I use lines created using bitmap. Now user can move and rotate this line in anywhere inside panel which has that image as background. Now I have already tried to make panel double buffered which solves the problem of background snapshot while rotating, but when I move that line it takes a piece of background with it. I also tried to refresh panel but that's not working.
Heres the code:
private bool _rotating;
private bool _dragging;
private int _clickX;
private int _clickY;
private Point _clickCoords;
private int _middleX;
private int _middleY;
private int _centerX;
private int _centerY;
private double _radius;
private DateTime lastRotate = DateTime.Today;
public void UpdateCircle(Line l)
{
_centerX = (l.EndPoint.X + l.StartPoint.X) / 2 + l.Left;
_centerY = (l.EndPoint.Y + l.StartPoint.Y) / 2 + l.Top;
_radius = GetDistance(l.StartPoint.X, l.StartPoint.Y, l.EndPoint.X, l.EndPoint.Y) / 2;
// panel5.Refresh();
}
public void UpdateMiddle(Line l)
{
_middleX = (l.EndPoint.X + l.StartPoint.X) / 2;
_middleY = (l.EndPoint.Y + l.StartPoint.Y) / 2;
// panel5.Refresh();
}
public double GetDistance(double x1, double y1, double x2, double y2)
{
return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
public void LineMouseDown(object sender, MouseEventArgs e)
{
var l = (Line)sender;
if (GetDistance(e.X, e.Y, l.StartPoint.X, l.StartPoint.Y) < 30 ||
GetDistance(e.X, e.Y, l.EndPoint.X, l.EndPoint.Y) < 30)
{
_rotating = true;
}
else
{
_dragging = true;
panel5.Refresh();
}
_clickX = e.X;
_clickY = e.Y;
panel5.Refresh();
_clickCoords = new Point(l.Left + e.X, l.Top + e.Y);
UpdateCircle(l);
UpdateMiddle(l);
// panel5.Refresh();
}
public void LineMouseUp(object sender, MouseEventArgs e)
{
_rotating = false;
_dragging = false;
panel5.Refresh();
}
public void LineMouseMove(object sender, MouseEventArgs e)
{
// panel5.Refresh();
var l = (Line)sender;
if (_rotating)
{
if ((DateTime.Now - lastRotate).TotalMilliseconds > 60)
{
try
{
var angle = GetAngle(l.Left + e.X, l.Top + e.Y, _centerX, 0, _centerX, _centerY);
var newStartPoint = new Point(
(int)Math.Round(_middleX + _radius * Math.Sin(angle)),
(int)Math.Round(_middleY + _radius * Math.Cos(angle))
);
var newEndPoint = new Point(2 * _middleX - newStartPoint.X, 2 * _middleY - newStartPoint.Y);
l.SetPoints(newStartPoint, newEndPoint);
UpdateMiddle(l);
lastRotate = DateTime.Now;
}
catch
{
//ignored
}
}
}
else if (_dragging)
{
l.Left = l.Left + e.X - _clickX;
l.Top = l.Top + e.Y - _clickY;
// panel5.Refresh();
}
}
public double GetAngle(int x2, int y2, int x1, int y1, int x0, int y0)
{
double angle = Math.Atan2(y1 - y0, x1 - x0) - Math.Atan2(y2 - y0, x2 - x0);
return angle;
}
And heres how I create lines:
bitLine = new Line { LineColor = Color.Red, LineWidth = 2, StartPoint = new Point(x1, y1), EndPoint = new Point(x2, y2) };
bitLine2 = new Line { LineColor = Color.Red, LineWidth = 2, StartPoint = new Point(x1, y1), EndPoint = new Point(x2, y2) };
//bitLine.Cursor = Cursors.SizeAll;
bitLine.MouseDown += LineMouseDown;
bitLine.MouseUp += LineMouseUp;
bitLine.MouseMove += LineMouseMove;
bitLine2.MouseDown += LineMouseDown;
bitLine2.MouseUp += LineMouseUp;
bitLine2.MouseMove += LineMouseMove;
bitLine2.Click += bitLine2_Click;
bitLine.Click += bitLine_Click;
panel5.Controls.Add(bitLine);
panel5.Controls.Add(bitLine2);
pb.WireControl(bitLine);
pb.WireControl(bitLine2);
bitLine.Location = Program.line1_location;
bitLine2.Location = Program.line2_location;
So please any suggestions will be helpful. I am really stuck in this.
Here is screenshot of what is the problem
When my windows form loads the tool strip menu item bar is there but the items inside it are not shown until I hover over them or press alt.
Also, my colordialog box wont open until after I click the change color icon in my menu and then press Alt.
I've had a look through the properties to make sure there is no hide property on or something but I can't seem to find anything.
I've posted some links below to show you what's happening.
When it opens up: http://gyazo.com/c1f4fa4d27e3f54a65b1f8a3da6cd0da
When I press alt or hover over menu: http://gyazo.com/c86c5dfb723647c025f89a0c14da6766
Not sure what code to put in.
Changing color bit:
private void changeColourToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult result = colorDialog1.ShowDialog();
}
I just dragged the elements on from the toolbox.
All Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Assignment
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
init();
start();
this.DoubleBuffered = true;
}
//code to convert HSB to RGB from HSB.cs. All your code so i made it take up less space.
public struct HSBColor
{
float h;
float s;
float b;
int a;
public HSBColor(float h, float s, float b) { this.a = 0xff; this.h = Math.Min(Math.Max(h, 0), 255); this.s = Math.Min(Math.Max(s, 0), 255); this.b = Math.Min(Math.Max(b, 0), 255); }
public HSBColor(int a, float h, float s, float b) { this.a = a; this.h = Math.Min(Math.Max(h, 0), 255); this.s = Math.Min(Math.Max(s, 0), 255); this.b = Math.Min(Math.Max(b, 0), 255); }
public float H { get { return h; } }
public float S { get { return s; } }
public float B { get { return b; } }
public int A { get { return a; } }
public Color Color { get { return FromHSB(this); } }
public static Color FromHSB(HSBColor hsbColor)
{
float r = hsbColor.b;
float g = hsbColor.b;
float b = hsbColor.b;
if (hsbColor.s != 0)
{
float max = hsbColor.b; float dif = hsbColor.b * hsbColor.s / 255f; float min = hsbColor.b - dif; float h = hsbColor.h * 360f / 255f;
if (h < 60f) { r = max; g = h * dif / 60f + min; b = min; }
else if (h < 120f) { r = -(h - 120f) * dif / 60f + min; g = max; b = min; }
else if (h < 180f) { r = min; g = max; b = (h - 120f) * dif / 60f + min; }
else if (h < 240f) { r = min; g = -(h - 240f) * dif / 60f + min; b = max; }
else if (h < 300f) { r = (h - 240f) * dif / 60f + min; g = min; b = max; }
else if (h <= 360f) { r = max; g = min; b = -(h - 360f) * dif / 60 + min; }
else { r = 0; g = 0; b = 0; }
}
return Color.FromArgb(hsbColor.a, (int)Math.Round(Math.Min(Math.Max(r, 0), 255)), (int)Math.Round(Math.Min(Math.Max(g, 0), 255)), (int)Math.Round(Math.Min(Math.Max(b, 0), 255)));
}
}
private const int MAX = 256; // max iterations
private const double SX = -2.025; // start value real
private const double SY = -1.125; // start value imaginary
private const double EX = 0.6; // end value real
private const double EY = 1.125; // end value imaginary
private static int x1, y1, xs, ys, xe, ye;
private static double xstart, ystart, xende, yende, xzoom, yzoom;
private static float xy;
private int c = 0;
//private Image picture; Taken out, not needed
// create rectangle variable JGB
Rectangle rec;
private Graphics g1;
//private Cursor c1, c2; Taken out, not needed
private System.Drawing.Bitmap bitmap;
public void init()
{
//setSize(640, 480); changed this code to JGB:
this.Size = new Size(640, 480);
// Taken all lines out below. Not needed.
/*finished = false;
addMouseListener(this);
addMouseMotionListener(this);
c1 = new Cursor(Cursor.WAIT_CURSOR);
c2 = new Cursor(Cursor.CROSSHAIR_CURSOR); */
x1 = 640;
y1 = 480;
xy = (float)x1 / (float)y1;
//picture = createImage(x1, y1); Taken out and replaced with JGB:
bitmap = new Bitmap(x1, y1);
//g1 = picture.getGraphics(); changed to get my bitmap
g1 = Graphics.FromImage(bitmap);
//finished = true; Finished variable deleted so not needed
}
//Code below didnt appear to do anything so i deleted it
/*public void destroy() // delete all instances
{
if (finished)
{
removeMouseListener(this);
removeMouseMotionListener(this);
picture = null;
g1 = null;
c1 = null;
c2 = null;
System.gc(); // garbage collection
}
} */
public void start()
{
//action = false;
//rectangle = false;
initvalues();
// added dialog box for instance loading and save varaibles needed for position and zoom to text file
DialogResult dialog = MessageBox.Show("Would You Like to Load Your Last Instance?", "Load Instance?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (dialog == DialogResult.Yes)
{
string[] lines = System.IO.File.ReadAllLines(#"C:\Users\Public\Writelines.txt");
xzoom = System.Convert.ToDouble(lines[0]);
yzoom = System.Convert.ToDouble(lines[1]);
xstart = System.Convert.ToDouble(lines[2]);
ystart = System.Convert.ToDouble(lines[3]);
}
else
{
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
}
mandelbrot();
}
public void stop()
{
}
/*public void paint(Graphics g, PaintEventArgs e)
{
update(g);
}
public void update(Graphics g)
{
//g.DrawImage(picture, 0, 0);
}*/
private void mandelbrot()
{
int x, y;
float h, b, alt = 0.0f;
Color color;
Pen pen = new Pen(Color.Black);
for (x = 0; x < x1; x += 2)
for (y = 0; y < y1; y++)
{
h = pointcolour(xstart + xzoom * (double)x, ystart + yzoom * (double)y, c);
if (h != alt)
{
b = 1.0f - h * h;
color = HSBColor.FromHSB(new HSBColor(h * 255, 0.8f * 255, b * 255));
pen = new Pen(color);
alt = h;
}
g1.DrawLine(pen, x, y, x + 1, y);
}
}
private float pointcolour(double xwert, double ywert, int j)
{
double r = 0.0, i = 0.0, m = 0.0;
// int j = 0;
while ((j < MAX) && (m < 4.0))
{
j++;
m = r * r - i * i;
i = 2.0 * r * i + ywert;
r = m + xwert;
}
return (float)j / (float)MAX;
}
private void initvalues()
{
xstart = SX;
ystart = SY;
xende = EX;
yende = EY;
if ((float)((xende - xstart) / (yende - ystart)) != xy)
xstart = xende - (yende - ystart) * (double)xy;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
using (Pen pen = new Pen(Color.White, 2))
{
e.Graphics.DrawRectangle(pen, rec);
}
Invalidate();
}
//added load method
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
xe = e.X;
ye = e.Y;
if (xs < xe)
{
if (ys < ye) rec = new Rectangle(xs, ys, (xe - xs), (ye - ys));
else rec = new Rectangle(xs, ye, (xe - xs), (ys - ye));
}
else
{
if (ys < ye) rec = new Rectangle(xe, ys, (xs - xe), (ye - ys));
else rec = new Rectangle(xe, ye, (xs - xe), (ys - ye));
}
this.Invalidate();
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// e.consume();
xs = e.X;
ys = e.Y; // starting point y
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
rec = new Rectangle(0, 0, 0, 0);
if (e.Button == MouseButtons.Left)
{
int z, w;
//e.consume();
//xe = e.X;
//ye = e.Y;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
string stringxzoom = xzoom.ToString();
string stringyzoom = yzoom.ToString();
string stringystart = ystart.ToString();
string stringxstart = xstart.ToString();
string[] lines = { stringxzoom, stringyzoom, stringxstart, stringystart };
System.IO.File.WriteAllLines(#"C:\Users\Public\Writelines.txt", lines);
this.Invalidate();
//Repaint();
}
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
dialog.InitialDirectory =System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);
dialog.Title = "Save Image";
dialog.FileName = "";
dialog.Filter = "JPEG|*.jpg";
dialog.ShowDialog();
}
private void restartToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void changeColourToolStripMenuItem_Click(object sender, EventArgs e)
{
colorDialog1.ShowDialog();
}
private void menuToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
}
}
Ok so after hours of trying to figure this out i found the solution. A random invalidate was messing it up.
Thanks anyway to #Sjips for the fast help.
I am writing an app which allow user to draw on a touch screen display. I am currently using the method below and it work very well. This method is producing a “high resolution image” since for almost every single pixel a line is drawn (e.g. 100, 100 -> 102, 103).
Here is my question. I’d like user to draw a “low resolution picture” (big pixels board) where you can intentionally see pixels of 50×50 (e.g. 100, 100 -> 150, 150). Does anybody have an idea on how to accomplish that? I am using Silverlight for Windows Phone. I was thinking about building a big grid of 50×50 pixels, but there might be too many controls.
void FingerMove(object sender, MouseEventArgs e)
{
if (this.IsDrawing)
{
this.DestinationPoint = e.GetPosition(paint);
Line line = new Line
{
Stroke = this.Color,
X1 = this.DestinationPoint.X,
Y1 = this.DestinationPoint.Y,
X2 = this.OriginPoint.X,
Y2 = this.OriginPoint.Y,
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeThickness = 15,
Opacity = 1,
};
Debug.WriteLine(string.Join(",", line.X1, line.Y1, line.X2, line.Y2));
paint.Children.Add(line);
}
this.OriginPoint = this.DestinationPoint;
}
#Amr has the right idea. I'll give you this code with the caveat that I haven't tested it at all. I took the line segment intersection algorithm from here.
First, you need to set up a list of Rectangles and add them to the canvas that are your "pixels":
private List<Rectangle> _rects = new List<Rectangle>();
private void GenerateRects()
{
int width = 300; // or whatever dimensions...
int height = 300;
int gridSize = 50;
for (int x = 0; x < width; x += gridSize)
{
for (int y = 0; y < height; y += gridSize)
{
var rect = new Rectangle
{
Opacity = 0,
Width = Math.Min(gridSize, width - x),
Height = Math.Min(gridSize, height - y),
};
Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
_rects.Add(rect);
this.paint.Children.Add(rect);
}
}
}
We'll need these helper methods:
class LineSegment
{
public double X1 { get; set; }
public double X2 { get; set; }
public double Y1 { get; set; }
public double Y2 { get; set; }
}
private static bool SegmentsIntersect(LineSegment A, LineSegment B)
{
double x1 = A.X1, x2 = A.X2, x3 = B.X1, x4 = B.X2;
double y1 = A.Y1, y2 = A.Y2, y3 = B.Y1, y4 = B.Y2;
double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denominator == 0)
return false;
double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
return (ua > 0 && ua < 1 && ub > 0 && ub < 1);
}
private static bool RectIntersectsLine(Rect A, LineSegment B)
{
return (SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y, X2 = A.X, Y2 = A.Y + A.Height }) ||
SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y + A.Height }) ||
SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y }) ||
SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y, X2 = A.X, Y2 = A.Y }) ||
RectContainsPoint(A, new Point(B.X1, B.Y1)) ||
RectContainsPoint(A, new Point(B.X2, B.Y2)));
}
private static bool RectContainsPoint(Rect A, Point B)
{
return (B.X > A.X && B.X < A.X + A.Width && B.Y > A.Y && B.Y < A.Y + A.Height);
}
Then, in the FingerMove function, we loop through each Rectangle to see if it intersects. If it does, we change its color:
void FingerMove(object sender, MouseEventArgs e)
{
if (this.IsDrawing)
{
this.DestinationPoint = e.GetPosition(paint);
LineSegment line = new LineSegment
{
X1 = this.DestinationPoint.X,
Y1 = this.DestinationPoint.Y,
X2 = this.OriginPoint.X,
Y2 = this.OriginPoint.Y
};
foreach (var rect in _rects)
{
var x = Canvas.GetLeft(rect);
var y = Canvas.GetTop(rect);
if (RectIntersectsLine(new Rect(x, y, rect.Width, rect.Height) , line))
{
rect.Opacity = 1;
rect.Fill = Color;
}
}
}
this.OriginPoint = this.DestinationPoint;
}
If you simply want to make the line thicker, just experiment with possible values of StrokeThickness untill you get the desired effect.
If you want to manually draw the line by filling up large areas of the screen say (50x50) rectangles, you could do the follwing:
divide the screen into 50x50 rectangles
check which rectangles are intersected by the line drawn by the user
only draw the rectangles from step 2
This would give you the 'snap to grid' line that you want.