//Constructor public Image(string path, string hash, WordsearchImage[] wordsearchImages, Dictionary<string, string> metaData) { this.Path = path; this.Hash = hash; this.WordsearchImages = wordsearchImages; this.MetaData = metaData; }
private void btnNextWordsearch_Click(object sender, EventArgs e) { //If there is a Bitmap to dispose of if (currentWordsearchImage != null) { //Dispose of bitmap(s) derived from WordsearchImage bitmap if(picBoxWordsearchImage.Image != currentBitmapWithGrid) { picBoxWordsearchImage.Image.Dispose(); } currentBitmapWithGrid.Dispose(); currentBitmapWithGrid = null; currentWordsearchId = null; currentWordsearchImage = null; } //If there is another wordsearch to mark up if (toProcess.Count > 0) { //Get the next wordsearch ID currentWordsearchId = toProcess.Dequeue(); //Get the clearest image of this wordsearch currentWordsearchImage = ImageMarkupDatabase.GetClearestWordsearchImage(currentWordsearchId); //Get the bitmap of this wordsearch image and draw the grid on it currentBitmapWithGrid = currentWordsearchImage.GetBitmapCustomResolution( WORDSEARCH_IMAGE_BITMAP_WIDTH, WORDSEARCH_IMAGE_BITMAP_HEIGHT); DrawGrid.GridInPlace(currentBitmapWithGrid, currentWordsearchImage.Rows, currentWordsearchImage.Cols); //Highlight 0, 0 (current index) & display the image highlightCharacter(0, 0); //Update the label showing how many wordsearches are left to be processed updateLblToProcess(); //Update the label showing the wordsearch ID lblWordsearchId.Text = defaultLblWordsearchId + currentWordsearchId; //Reset all fields resetFields(); } else //Otherwise there are no more wordsearches to mark up { picBoxWordsearchImage.Visible = false; lblWordsearchId.Text = defaultLblWordsearchId; } }
//Private Methods private static void loadDatabase() { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(XML_DOC_LOCATION); XmlNode rootNode = xmlDoc.SelectSingleNode(XML_ROOT_EL); //Images XmlNode imagesNode = rootNode.SelectSingleNode(XML_IMAGES_EL); XmlNodeList imageNodes = imagesNode.SelectNodes(XML_IMAGE_EL); images = new Dictionary<string, Image>(); foreach(XmlNode imageNode in imageNodes) { string path = imageNode.SelectSingleNode(XML_IMAGE_FILE_PATH_EL).InnerText; string hash = imageNode.SelectSingleNode(XML_IMAGE_HASH_EL).InnerText; //Wordsearch Images XmlNode wordsearchImagesNode = imageNode.SelectSingleNode(XML_WORDSEARCH_IMAGES_EL); XmlNodeList wordsearcheImageNodes = wordsearchImagesNode.SelectNodes(XML_WORDSEARCH_IMAGE_EL); List<WordsearchImage> wordsearchImages = new List<WordsearchImage>(); foreach(XmlNode wiNode in wordsearcheImageNodes) { //Coordinates string strCoords = wiNode.SelectSingleNode(XML_WORDSEARCH_IMAGE_COORDINATES_EL).InnerText; string[] pairs = strCoords.Trim().Split('.'); char[] trimChars = new char[] { '(', ')', ' ' }; string[] parts = pairs[0].Split(','); string x = parts[0].Trim(trimChars); string y = parts[1].Trim(trimChars); IntPoint topLeft = new IntPoint(int.Parse(x), int.Parse(y)); parts = pairs[1].Split(','); x = parts[0].Trim(trimChars); y = parts[1].Trim(trimChars); IntPoint topRight = new IntPoint(int.Parse(x), int.Parse(y)); parts = pairs[2].Split(','); x = parts[0].Trim(trimChars); y = parts[1].Trim(trimChars); IntPoint bottomRight = new IntPoint(int.Parse(x), int.Parse(y)); parts = pairs[3].Split(','); x = parts[0].Trim(trimChars); y = parts[1].Trim(trimChars); IntPoint bottomLeft = new IntPoint(int.Parse(x), int.Parse(y)); uint rows = uint.Parse(wiNode.SelectSingleNode(XML_WORDSEARCH_IMAGE_ROWS_EL).InnerText); uint cols = uint.Parse(wiNode.SelectSingleNode(XML_WORDSEARCH_IMAGE_COLS_EL).InnerText); //Meta Data XmlNode metaDataNode = wiNode.SelectSingleNode(XML_WORDSEARCH_IMAGE_METADATA_EL); XmlNodeList metaDataNodes = metaDataNode.ChildNodes; Dictionary<string, string> metaData = new Dictionary<string, string>(); foreach(XmlNode metaDataEntryNode in metaDataNodes) { metaData.Add(metaDataEntryNode.Name, metaDataEntryNode.InnerText); } //Optional WordsearchID field string wordsearchId = null; try { wordsearchId = wiNode.SelectSingleNode(XML_WORDSEARCH_IMAGE_WORDSEARCH_ID_EL).InnerText; } catch { } //If there was a Wordsearch ID, pass that to the constructor WordsearchImage wordsearchImage; if(wordsearchId != null) { wordsearchImage = new WordsearchImage(topLeft, topRight, bottomRight, bottomLeft, rows, cols, metaData, hash, wordsearchId); } else //Otherwise there is no Wordsearch ID { wordsearchImage = new WordsearchImage(topLeft, topRight, bottomRight, bottomLeft, rows, cols, metaData, hash); } wordsearchImages.Add(wordsearchImage); } //Meta Data XmlNodeList imageMetaDataNodes = imageNode.SelectSingleNode(XML_IMAGE_METADATA_EL).ChildNodes; Dictionary<string, string> imageMetaData = new Dictionary<string, string>(); foreach(XmlNode metaDataEntryNode in imageMetaDataNodes) { imageMetaData.Add(metaDataEntryNode.Name, metaDataEntryNode.InnerText); } Image image = new Image(path, hash, wordsearchImages.ToArray(), imageMetaData); images.Add(hash, image); } //Wordsearches XmlNode wordsearchesNode = rootNode.SelectSingleNode(XML_WORDSEARCHES_EL); XmlNodeList wordsearchNodes = wordsearchesNode.SelectNodes(XML_WORDSEARCH_EL); wordsearches = new Dictionary<string, Wordsearch>(); foreach(XmlNode wordsearchNode in wordsearchNodes) { string wordsearchId = wordsearchNode.SelectSingleNode(XML_WORDSEARCH_ID_EL).InnerText; //Chars string strChars = wordsearchNode.SelectSingleNode(XML_WORDSEARCH_CHARS_EL).InnerText.Replace("\r", ""); //Strip carriage returns string[] strCharsRows = strChars.Split('\n'); char[,] chars = new char[strCharsRows[0].Length, strCharsRows.Length]; for(int i = 0; i < strCharsRows.Length; i++) { for(int j = 0; j < strCharsRows[i].Length; j++) { chars[j, i] = strCharsRows[i][j]; } } //Words XmlNode wordsNode = wordsearchNode.SelectSingleNode(XML_WORDSEARCH_WORDS_EL); XmlNodeList wordNodes = wordsNode.SelectNodes(XML_WORDSEARCH_WORD_EL); List<string> words = new List<string>(); foreach(XmlNode wordNode in wordNodes) { words.Add(wordNode.InnerText); } Wordsearch wordsearch = new Wordsearch(wordsearchId, chars, words.ToArray()); wordsearches.Add(wordsearchId, wordsearch); } }
internal static bool IsWordsearch(List<IntPoint> candidate, WordsearchImage wordsearchImage) { IntPoint[] wordsearchPoints = wordsearchImage.Coordinates; //Does the candidate match this wordsearch (allowing for some margin of error so the points needn't be pixel perfect) //Margin for error should be an average cell dimension for a wordsearch grid in a circle around each corner of the wordsearch double widthTop = Geometry.EuclideanDistance(wordsearchImage.TopLeft, wordsearchImage.TopRight); double widthBottom = Geometry.EuclideanDistance(wordsearchImage.BottomLeft, wordsearchImage.BottomRight); double avgWidth = (widthTop + widthBottom) / 2; double avgCellWidth = avgWidth / wordsearchImage.Cols; double heightLeft = Geometry.EuclideanDistance(wordsearchImage.TopLeft, wordsearchImage.BottomLeft); double heightRight = Geometry.EuclideanDistance(wordsearchImage.TopRight, wordsearchImage.BottomRight); double avgHeight = (heightLeft + heightRight) / 2; double avgCellHeight = avgHeight / wordsearchImage.Rows; double errorMarginRadius = (avgCellWidth + avgCellHeight) / 2; //CandidatePoint => WordsearchPoint List<List<int>> matchedPoints = new List<List<int>>(candidate.Count); for (int i = 0; i < candidate.Count; i++) { matchedPoints.Add(new List<int>()); } //For each candidate point, attempt to match each actual wordsearch point, storing the matches bool brokeEarly = false; for (int i = 0; i < candidate.Count; i++) { for (int j = 0; j < wordsearchPoints.Length; j++) { IntPoint candidatePoint = candidate[i]; IntPoint wordsearchPoint = wordsearchPoints[j]; double pointDistance = Geometry.EuclideanDistance(candidatePoint, wordsearchPoint); if (pointDistance <= errorMarginRadius) { matchedPoints[i].Add(j); } } //If there were no wordsearch points that could be matched to this candidate point, fail for this wordsearch if (matchedPoints[i].Count == 0) { brokeEarly = true; break; } } //If we already know for sure that this isn't a match, continue to the next wordsearch if (brokeEarly) { return false; } //Each point matched to another, now to check that there is a way for each point to mapped onto a distinct point in the other set List<List<int>> pointMatchCandidates = new List<List<int>>(); //Initialise the search with the first possible matches foreach (int n in matchedPoints[0]) { List<int> li = new List<int>(); li.Add(n); pointMatchCandidates.Add(li); } for (int i = 1; i < matchedPoints.Count; i++) { List<List<int>> newCandidates = new List<List<int>>(); foreach (int wordsearchPoint in matchedPoints[i]) { for (int j = 0; j < pointMatchCandidates.Count; j++) { //If this branch of the tree doesn't already contain this point, add it as it is a possible next branch to a solution if (!pointMatchCandidates[j].Contains(wordsearchPoint)) { List<int> copied = copy(pointMatchCandidates[j]); copied.Add(wordsearchPoint); newCandidates.Add(copied); } } } //Update the list of candidates pointMatchCandidates = newCandidates; } //If we are left with any solutions, then the candidate matches the wordsearch if (pointMatchCandidates.Count > 0) { return true; } else { return false; } }
private void btnAddWordsearchImage_Click(object sender, EventArgs e) { //Check that we currently have an image on screen bool showingBitmap = !currentBitmap.Equals(default(KeyValuePair<Bitmap, string>)); if(showingBitmap) { //Fetch all of the data for the Wordsearch Image & Validate it //Coordinates IntPoint topLeft = new IntPoint(); //Must have defaults to compile IntPoint topRight = new IntPoint(); IntPoint bottomRight = new IntPoint(); IntPoint bottomLeft = new IntPoint(); bool validCoords = false; try { topLeft = getTopLeftCoordinate(); topRight = getTopRightCoordinate(); bottomRight = getBottomRightCoordinate(); bottomLeft = getBottomLeftCoordinate(); //Check that the values we have received are in bounds IntPoint[] coords = new IntPoint[4]; coords[0] = topLeft; //Winding Order: Clockwise starting in top left coords[1] = topRight; coords[2] = bottomRight; coords[3] = bottomLeft; foreach (IntPoint p in coords) { if (p.X < 0 || p.Y < 0 || p.X >= currentBitmap.Key.Width || p.Y >= currentBitmap.Key.Height) { throw new Exception("Point outside of Bitmap Bounds"); } } validCoords = true; } catch { MessageBox.Show("Error parsing coordinates: please correct them and try again"); } //If the coordinates are valid, get the next lot of data if (validCoords) { //Num rows & cols uint rows, cols; bool parsedRows = uint.TryParse(txtNumRows.Text, out rows); bool parsedCols = uint.TryParse(txtNumCols.Text, out cols); bool validRowsAndCols = parsedRows && parsedCols && rows != 0 && cols != 0; if (validRowsAndCols) { //Meta Data Dictionary<string, string> metaData = new Dictionary<string, string>(); bool validMetaData = true; for (int i = 0; i < dataGridViewWordsearchImageMetaData.Rows.Count; i++) { DataGridViewCell keyCell = dataGridViewWordsearchImageMetaData.Rows[i].Cells[0]; //If this cell has a value (i.e. is not the blank cell auto-inserted at the end) if (keyCell.Value != null) { string key = keyCell.Value.ToString().Trim(); //Check the key isn't blank if (key != "") { //Check the key isn't already taken if (!metaData.ContainsKey(key)) { DataGridViewCell valueCell = dataGridViewWordsearchImageMetaData.Rows[i].Cells[1]; //Check that this has a value, if not make it empty string value; if(valueCell.Value == null) { value = ""; } else { value = valueCell.Value.ToString(); } metaData.Add(key, value); } else //Otherwise the key is already taken { validMetaData = false; MessageBox.Show("Invalid Meta Data: You cannot use the same key twice (" + key + ")"); break; } } else //Otherwise the key is blank { validMetaData = false; MessageBox.Show("Invalid Meta Data: You cannot have a blank Key"); break; } } } //If the Meta Data is valid, we have everything necessary to make a WordsearchImage if (validMetaData) { //Make a WordsearchImage string fromImageHash = currentBitmap.Value; WordsearchImage wordsearchImage; //Check for optional field Wordsearch ID string wordsearchId = txtWordsearchId.Text.Trim(); if (wordsearchId != "") { wordsearchImage = new WordsearchImage(topLeft, topRight, bottomRight, bottomLeft, rows, cols, metaData, fromImageHash, wordsearchId); } else //Otherwise the optional Wordsearch ID field hasn't been filled in { wordsearchImage = new WordsearchImage(topLeft, topRight, bottomRight, bottomLeft, rows, cols, metaData, fromImageHash); } wordsearchImages.Add(wordsearchImage); //Reset the fields ready for the next WordsearchImage to be entered resetWordsearchImageFields(); //If the image being displayed isn't being used elsewhere, dispose of it if(bitmapToShow != picBoxImage.Image) { picBoxImage.Image.Dispose(); } //Draw all of the WordsearchImages for this Image onto the original bitmap and display it bitmapToShow = drawWordsearchImagesOnCurrentWordsearch(); picBoxImage.Image = bitmapToShow; //If we were showing an enlarged wordsearch image, switch back to the main image if(picBoxWordsearchImageLarge.Visible) { picBoxImage.Visible = true; picBoxWordsearchImageLarge.Visible = false; } } } else { MessageBox.Show("Error parsing number of rows and cols: please correct them and try again"); } } } else //Otherwise we aren't showing a bitmap { MessageBox.Show("No Image currently on screen to add a Word search Image to"); } }