internal void SwapOrientation(IList <WordAndCanvas> wordAndCanvasList, bool memorizeForUndo) { Debug.Assert(wordAndCanvasList != null && wordAndCanvasList.Count == 1); WordAndCanvas wac = wordAndCanvasList.First(); if (memorizeForUndo) { viewModel.UndoStack.MemorizeSwapOrientation(wordAndCanvasList); viewModel.RemoveWordPosition(wac.WordPosition); wac.WordPosition.SetNewPositionOrientation(new PositionOrientation(wac.WordPosition.StartRow, wac.WordPosition.StartColumn, !wac.WordPosition.IsVertical)); // Do not accept Illegal placements, adjust to only valid placements WordPositionLayout layout = viewModel.GetLayoutExcludingWordPosition(wac.WordPosition); List <PositionOrientation> topLeftList = new List <PositionOrientation> { new PositionOrientation(wac.WordPosition.PositionOrientation) }; AdjustToSuitableLocationInLayout(layout, wordAndCanvasList, topLeftList, true); wac.WordPosition.SetNewPositionOrientation(topLeftList[0]); viewModel.AddWordPosition(wac.WordPosition); } wac.RebuildCanvasAfterOrientationSwap(); // Only relocate visually letters of the word MoveWordAndCanvasList(wordAndCanvasList); // Visual animation FinalRefreshAfterUpdate(); }
// If position is not valid, look around until a valid position is found // Examine surrounding cells in a "snail pattern" private void AdjustToSuitableLocationInLayout(WordPositionLayout layout, IList <WordAndCanvas> wordAndCanvasList, IList <PositionOrientation> topLeftList, bool onlyValidPlacement) { // Internal helper to check all words bool CanPlaceAllWords(bool isOnlyValidPlacement) { for (int il = 0; il < wordAndCanvasList.Count; il++) { var placmentStatus = EditorViewModel.CanPlaceWordAtPositionInLayout(layout, wordAndCanvasList[il].WordPosition, topLeftList[il]); if (placmentStatus == PlaceWordStatus.Invalid || placmentStatus == PlaceWordStatus.TooClose && !isOnlyValidPlacement) { return(false); } } return(true); } if (!CanPlaceAllWords(onlyValidPlacement)) { int st = 1; int sign = 1; for (; ;) { for (int i = 0; i < st; i++) { for (int il = 0; il < wordAndCanvasList.Count; il++) { topLeftList[il] = new PositionOrientation(topLeftList[il].StartRow, topLeftList[il].StartColumn + sign, m_Sel.WordAndCanvasList[il].WordPosition.IsVertical); } if (CanPlaceAllWords(true)) { return; } } for (int i = 0; i < st; i++) { for (int il = 0; il < wordAndCanvasList.Count; il++) { topLeftList[il] = new PositionOrientation(topLeftList[il].StartRow + sign, topLeftList[il].StartColumn, m_Sel.WordAndCanvasList[il].WordPosition.IsVertical); } if (CanPlaceAllWords(true)) { return; } } sign = -sign; st++; } } }
// Build a copy of Layout without a specific list of WordPosition to validate placement internal WordPositionLayout GetLayoutExcludingWordPositionList(IEnumerable <WordPosition> movedWordPositionList) { var layout = new WordPositionLayout(); foreach (var wp in Layout.WordPositionList) { // ReSharper disable once PossibleMultipleEnumeration if (!movedWordPositionList.Contains(wp)) { layout.AddWordPositionNoCheck(wp); } } return(layout); }
// Separate from MainGrid_MouseDown to reduce complexity private Action <Point> GetMouseDownMoveAction() { // Need a layout without moved word to validate placement m_FixedLayout = viewModel.GetLayoutExcludingWordPositionList(m_Sel.WordAndCanvasList.Select(wac => wac.WordPosition)); // Reverse-transform mouse Grid coordinates into DrawingCanvas coordinates Matrix m = MainMatrixTransform.Matrix; m.Invert(); // To convert from screen transformed coordinates into ideal grid // coordinates starting at (0,0) with a square side of UnitSize List <Vector> clickOffsetList = new List <Vector>(m_Sel.WordAndCanvasList.Count); clickOffsetList.AddRange(m_Sel.WordAndCanvasList .Select(wac => new Point((double)wac.WordCanvas.GetValue(Canvas.LeftProperty), (double)wac.WordCanvas.GetValue(Canvas.TopProperty))) .Select(canvasTopLeft => canvasTopLeft - m.Transform(previousMousePosition))); // When moving, point is current mouse in ideal grid coordinates return(point => { // Just move selected WordCanvas for (int i = 0; i < m_Sel.WordAndCanvasList.Count; i++) { double preciseTop = point.Y + clickOffsetList[i].Y; double preciseLeft = point.X + clickOffsetList[i].X; WordCanvas wc = m_Sel.WordAndCanvasList[i].WordCanvas; wc.SetValue(Canvas.TopProperty, preciseTop); wc.SetValue(Canvas.LeftProperty, preciseLeft); // Round position to closest square on the grid int top = (int)Math.Floor(preciseTop / UnitSize + 0.5); int left = (int)Math.Floor(preciseLeft / UnitSize + 0.5); PlaceWordStatus status = EditorViewModel.CanPlaceWordAtPositionInLayout(m_FixedLayout, m_Sel.WordAndCanvasList[i].WordPosition, new PositionOrientation(top, left, m_Sel.WordAndCanvasList[i].WordPosition.IsVertical)); RecolorizeWord(m_Sel.WordAndCanvasList[i], status); } }); }
internal static PlaceWordStatus CanPlaceWordInLayout(WordPositionLayout layout, WordAndCanvas wac) { return(layout.CanPlaceWord(wac.WordPosition, true)); }
internal static PlaceWordStatus CanPlaceWordAtPositionInLayout(WordPositionLayout layout, WordPosition wordPosition, PositionOrientation positionOrientation) { return(EditorModel.CanPlaceWordAtPositionInLayout(layout, wordPosition, positionOrientation)); }
// Check if a WordPosition can be placed at specific location in given layout internal static PlaceWordStatus CanPlaceWordAtPositionInLayout(WordPositionLayout layout, WordPosition wordPosition, PositionOrientation position) { WordPosition testWordPosition = new WordPosition(wordPosition.Word, wordPosition.OriginalWord, position); return(layout.CanPlaceWord(testWordPosition, true)); }