MainPage.xaml.cs
// http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging; // for WriteableBitmap
namespace Skeleton2D
{
public struct Point
{
public int x;
public int y;
}
public partial class MainPage : UserControl
{
WriteableBitmap m_bitmap;
int width = 400;
int height = 300;
int colorCorners, colorCircumference, colorBackground, colorInside, colorInside1, colorSkeleton;
int colorErase;
Point[] corners = new Point[100];
int numberCorners = 0;
Point insidePoint;
int wasButtonClicked = 0;
public MainPage()
{
InitializeComponent();
text1.Text = "Left click for each corner in clockwise direction (maximally 100 points)"
+ "\n" + "Then press the button.";
this.MouseLeftButtonDown += new MouseButtonEventHandler(MainPage_MouseLeftButtonDown);
m_bitmap = new WriteableBitmap(width, height);
image1.Source = m_bitmap;
colorBackground = RGBA(0, 200, 0, 255);
// setting the background
for (int i = 0; i < width * height; i++)
{
m_bitmap.Pixels[i] = colorBackground;
}
colorCircumference = RGBA(200, 0, 200, 255);
colorCorners = RGBA(0, 0, 200, 255);
colorInside = RGBA(200, 200, 0, 255);
colorInside1 = RGBA(200, 0, 0, 255);
colorSkeleton = RGBA(100, 100, 50, 255);
colorErase = RGBA(199, 199, 0, 255);
}
private void MainPage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int x, y;
switch (wasButtonClicked)
{
case 0:
{
x = (int)(e.GetPosition(image1).X);
y = (int)(e.GetPosition(image1).Y);
corners[numberCorners].x = x;
corners[numberCorners].y = y;
numberCorners++;
m_bitmap.Pixels[y * width + x] = colorCorners;
m_bitmap.Pixels[y * width + x + 1] = colorCorners;
m_bitmap.Pixels[y * width + x - 1] = colorCorners;
m_bitmap.Pixels[(y + 1) * width + x] = colorCorners;
m_bitmap.Pixels[(y - 1) * width + x] = colorCorners;
m_bitmap.Invalidate();
}
break;
case 1:
{
x = (int)(e.GetPosition(image1).X);
y = (int)(e.GetPosition(image1).Y);
insidePoint.x = x;
insidePoint.y = y;
m_bitmap.Pixels[y * width + x] = colorInside1;
m_bitmap.Pixels[y * width + x + 1] = colorInside1;
m_bitmap.Pixels[y * width + x - 1] = colorInside1;
m_bitmap.Pixels[(y + 1) * width + x] = colorInside1;
m_bitmap.Pixels[(y - 1) * width + x] = colorInside1;
m_bitmap.Invalidate();
}
break;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
switch (wasButtonClicked)
{
case 0:
{
//corners[0].x = 1; corners[0].y = 1;
//corners[1].x = 20; corners[1].y = 1;
//corners[2].x = 20; corners[2].y = 10;
//corners[3].x = 13; corners[3].y = 10;
//corners[4].x = 13; corners[4].y = 15;
//corners[5].x = 8; corners[5].y = 15;
//corners[6].x = 8; corners[6].y = 10;
//corners[7].x = 1; corners[7].y = 10;
DrawLine(corners[numberCorners - 1], corners[0]);
m_bitmap.Pixels[corners[numberCorners - 1].y * width + corners[numberCorners - 1].x] = colorCircumference;
for (int i = 0; i < numberCorners - 1; i++)
{
DrawLine(corners[i], corners[i + 1]);
m_bitmap.Pixels[corners[i].y * width + corners[i].x] = colorCircumference;
}
m_bitmap.Invalidate();
text1.Text = "One left click for the internal point and then press the button.";
wasButtonClicked++;
}
break;
case 1:
{
//insidePoint.x = 8; insidePoint.y = 8;
FillPolygon(insidePoint.x, insidePoint.y);
m_bitmap.Invalidate();
text1.Text = "Now press the button to see the skeleton.";
wasButtonClicked++;
}
break;
case 2:
{
FindSkeleton();
m_bitmap.Invalidate();
text1.Text = "Refresh the page to start again.";
}
break;
}
}
public void FindSkeleton()
{
int i, j, k;
bool stop = false;
int neighbours;
int[] points = new int[width * height];
while (stop == false)
{
for (k = 0; k < width * height; k++)
{
points[k] = 0;
}
stop = true;
for (i = 0; i < width; i++)
{
for (j = 0; j < height; j++)
{
neighbours = 0;
if (m_bitmap.Pixels[j * width + i] == colorCircumference)
{
if (m_bitmap.Pixels[j * width + i + 1] == colorInside)
{
neighbours++;
points[j * width + i + 1] = 1;
stop = false;
}
if (m_bitmap.Pixels[j * width + i - 1] == colorInside)
{
neighbours++;
points[j * width + i - 1] = 1;
stop = false;
}
if (m_bitmap.Pixels[(j + 1) * width + i] == colorInside)
{
neighbours++;
points[(j + 1) * width + i] = 1;
stop = false;
}
if (m_bitmap.Pixels[(j - 1) * width + i] == colorInside)
{
neighbours++;
points[(j - 1) * width + i] = 1;
stop = false;
}
if (m_bitmap.Pixels[(j + 1) * width + i + 1] == colorInside)
{
neighbours++;
stop = false;
}
if (m_bitmap.Pixels[(j + 1) * width + i - 1] == colorInside)
{
neighbours++;
stop = false;
}
if (m_bitmap.Pixels[(j - 1) * width + i + 1] == colorInside)
{
neighbours++;
stop = false;
}
if (m_bitmap.Pixels[(j - 1) * width + i - 1] == colorInside)
{
neighbours++;
stop = false;
}
if (neighbours <= 1)
{
m_bitmap.Pixels[j * width + i] = colorSkeleton;
}
else
{
// here comes code for connected skeleton
m_bitmap.Pixels[j * width + i] = colorErase;
}
} // if
} // loop in j
} // loop in i
for (i = 0; i < width; i++)
{
for (j = 0; j < height; j++)
{
if (points[j * width + i] == 1)
{
m_bitmap.Pixels[j * width + i] = colorCircumference;
points[j * width + i] = 0;
}
}
}
}
}
public void FillPolygon(int x, int y) // simple recursion leaded to stack overflow
{
int i, j, k;
int colorCurrent;
colorCurrent = m_bitmap.Pixels[y * width + x];
if ((colorCurrent == colorCircumference) || (colorCurrent == colorInside))
return;
i = 0;
while (true)
{
colorCurrent = m_bitmap.Pixels[y * width + x + i];
if ((colorCurrent != colorCircumference))
{
m_bitmap.Pixels[y * width + x + i] = colorInside;
if (m_bitmap.Pixels[(y - 1) * width + x + i] == colorCircumference
&& m_bitmap.Pixels[(y) * width + x + i - 1] == colorInside
&& m_bitmap.Pixels[(y) * width + x + i + 1] != colorCircumference)
{
if (m_bitmap.Pixels[(y - 1) * width + x + i + 1] == colorCircumference)
{
k = 0;
while (m_bitmap.Pixels[(y) * width + x + i + 1 + k] == colorCircumference)
{
k++;
}
FillPolygon(x + i + 1 + k, y - 1);
}
else FillPolygon(x + i + 1, y - 1);
}
if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference
&& m_bitmap.Pixels[(y + 1) * width + x + i - 1] == colorCircumference)
FillPolygon(x + i - 1, y);
if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference
&& m_bitmap.Pixels[(y) * width + x + i - 1] == colorCircumference)
return;
if (m_bitmap.Pixels[(y) * width + x + i + 1] == colorCircumference
&& m_bitmap.Pixels[(y + 1) * width + x + i] != colorCircumference)
FillPolygon(x + i, y + 1);
if (m_bitmap.Pixels[(y + 1) * width + x + i] == colorCircumference)
FillPolygon(x + i - 1, y + 1);
i += 1;
}
else
{
break;
}
}
j = 0;
while (true)
{
colorCurrent = m_bitmap.Pixels[y * width + x - j];
if ((colorCurrent != colorCircumference))
{
m_bitmap.Pixels[y * width + x - j] = colorInside;
if (m_bitmap.Pixels[(y + 1) * width + x - j] == colorCircumference
&& m_bitmap.Pixels[(y) * width + x - j + 1] == colorInside
&& m_bitmap.Pixels[(y) * width + x - j - 1] != colorCircumference)
{
FillPolygon(x - j - 1, y + 1);
}
if (m_bitmap.Pixels[(y - 1) * width + x - j] == colorCircumference
&& m_bitmap.Pixels[(y - 1) * width + x - j + 1] == colorCircumference)
{
k = 0;
while (m_bitmap.Pixels[(y - 1) * width + x - j + k] == colorCircumference
&& m_bitmap.Pixels[(y) * width + x - j + k] != colorCircumference)
{
k++;
}
FillPolygon(x - j + k, y - 1);
}
if (m_bitmap.Pixels[(y) * width + x - j - 1] == colorCircumference
&& m_bitmap.Pixels[(y - 1) * width + x - j] != colorCircumference)
FillPolygon(x - j, y - 1);
if (m_bitmap.Pixels[(y - 1) * width + x - j] == colorCircumference)
FillPolygon(x - j + 1, y - 1);
j += 1;
}
else
{
break;
}
}
}
//public void FillPolygon(int x, int y) // simple recursion leads to stack overflow
//{
// int colorCurrent;
// colorCurrent = m_bitmap.Pixels[y * width + x];
// if ( (colorCurrent == colorCircumference) | (colorCurrent == colorInside) )
// return;
// m_bitmap.Pixels[y * width + x] = colorInside;
// FillPolygon(x, y + 1);
// FillPolygon(x, y - 1);
// FillPolygon(x + 1, y);
// FillPolygon(x - 1, y);
//}
private void DrawLine(Point start, Point end)
{
float k, b;
float x, y;
int xint, xend, yint, yend;
int index;
if (start.x > end.x)
{
Swap(ref start.x, ref end.x);
Swap(ref start.y, ref end.y); // to have increasing x
}
if (end.x - start.x > Math.Abs(end.y - start.y)) // line: y = k * x + b
{
k = ((float)end.y - start.y) / (end.x - start.x);
b = start.y - k * start.x;
x = start.x;
xend = end.x;
if (k > 0)
{
y = k * x + b;
xint = (int)x;
while (xint < xend)
{
index = ((int)y) * (int)(width) + xint;
m_bitmap.Pixels[index] = colorCircumference;
xint += 1;
y += k;
}
}
else if (k < 0)
{
y = k * x + b;
xint = (int)x;
while (xint < xend)
{
index = ((int)y) * (int)(width) + xint;
m_bitmap.Pixels[index] = colorCircumference;
xint += 1;
y += k;
}
}
else // k = 0
{
y = b;
xint = (int)x;
while (xint < xend)
{
index = ((int)y) * (int)(width) + xint;
m_bitmap.Pixels[index] = colorCircumference;
xint += 1;
y += k;
}
}
}
else // line: x = k * y + b
{
if (start.y > end.y)
{
Swap(ref start.x, ref end.x);
Swap(ref start.y, ref end.y); // to have increasing y
}
k = ((float)end.x - start.x) / (end.y - start.y);
b = start.x - k * start.y;
y = start.y;
yend = end.y;
if (k > 0)
{
x = k * y + b;
yint = (int)y;
while (yint < yend)
{
index = yint * (int)(width) + ((int)x);
m_bitmap.Pixels[index] = colorCircumference;
yint += 1;
x += k;
}
}
else if (k < 0)
{
x = k * y + b;
yint = (int)y;
while (yint < yend)
{
index = yint * (int)(width) + ((int)x);
m_bitmap.Pixels[index] = colorCircumference;
yint += 1;
x += k;
}
}
else // k = 0
{
x = b;
yint = (int)y;
while (yint < yend)
{
index = yint * (int)(width) + ((int)x);
m_bitmap.Pixels[index] = colorCircumference;
yint += 1;
x += k;
}
}
}
}
static public void Swap(ref int a, ref int b)
{
int temp;
temp = a; a = b; b = temp;
}
public int RGBA(byte byRed, byte byGreen, byte byBlue, byte alpha)
{
return (alpha * 16777216) + (byRed * 65536) + (byGreen * 256) + byBlue;
}
}
}