private void Redo() { if (redoQueue.Count > 0) { BrushAction redoAction = redoQueue.Dequeue(); //Set all of the values in the affected area to what they were before the undo action took place. HeightRender render = Document.SelectedRender; BrushAction opposite = new BrushAction(redoAction.Selection, redoAction.Data); for (int x = redoAction.Selection.Left; x < redoAction.Selection.Right; x++) { for (int y = redoAction.Selection.Top; y < redoAction.Selection.Bottom; y++) { float oldSample; if (render.HeightField.TryGetHeight(x, y, out oldSample)) { int i = (y - redoAction.Selection.Top) * redoAction.Selection.Width + (x - redoAction.Selection.Left); render.HeightField[x, y] = redoAction.Data[i]; opposite.Data[i] = oldSample; } } } PhotonGradient gradient = Document.SelectedGradient; IEnumerable <HeightRender.Effect> effects; if (paintEffectsBox.Checked) { effects = Document.SelectedEffects; } else { effects = null; //Don't worry about effects if we're not painting with them. } //Update each section of the selection when wrapping has been accounted for. foreach (FieldSelection fs in redoAction.Selection.SubSelectionsOf(render.HeightField)) { //Omit any parts that have a width or height of zero. if (!fs.IsEmpty) { //Update the corresponding part of the render. render.UpdateArea(fs.Left, fs.Top, fs.Width, fs.Height, gradient, effects); //Invalidate the corresponding part of the image panel so that it redraws itself in realtime. Point start = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Left, fs.Top))); Point end = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Right, fs.Bottom))); renderArea.Invalidate(new Rectangle(Numerics.Max(start.X, 0), Numerics.Max(start.Y, 0), Numerics.Max(end.X, 0), Numerics.Max(end.Y, 0))); } } undoQueue.Enqueue(opposite); } }
private void Application_Idle(object sender, EventArgs e) { Vector2 pointOnRenderArea = Numerics.ToVector(renderArea.PointToClient(Control.MousePosition)); Vector2 pointOnRender = renderArea.ClientToImage(pointOnRenderArea); if (renderArea.Focused && Document.SelectedRender != null) { #region Panning if (MouseButtons == MouseButtons.Middle) { renderArea.ImageOffset += (pointOnRenderArea - lastPointOnRenderArea) / renderArea.ImageScale; } #endregion #region Brush HeightBrush activeBrush = null; if (MouseButtons == MouseButtons.Left) { activeBrush = Document.LeftBrush; } else if (MouseButtons == MouseButtons.Right) { activeBrush = Document.RightBrush; } if (activeBrush != null) { HeightRender render = Document.SelectedRender; PhotonGradient gradient = Document.SelectedGradient; IEnumerable <HeightRender.Effect> effects; if (paintEffectsBox.Checked) { effects = Document.SelectedEffects; } else { effects = null; } #region Process Steps Vector2 brushDelta = lastPointOnRender - pointOnRender; float strokeLength = (float)brushDelta.Length; float steps = strokeLength / activeBrush.Precision + 1; Vector2 brushStep = brushDelta * (1.0f / steps); for (int i = 0; i < steps; i++) { Vector2 brushPosition = pointOnRender + brushStep * i; #region Process Brush Paint try { FieldSelection brushArea; activeBrush.Paint(render.HeightField, (int)brushPosition.X, (int)brushPosition.Y, strokeLength, out brushArea); foreach (FieldSelection fs in brushArea.SubSelectionsOf(render.HeightField)) { if (!fs.IsEmpty) { render.UpdateArea(fs.Left, fs.Top, fs.Width, fs.Height, gradient, effects); Point start = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Left, fs.Top))); Point end = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Right, fs.Bottom))); renderArea.Invalidate(new Rectangle(Numerics.Max(start.X, 0), Numerics.Max(start.Y, 0), Numerics.Max(end.X, 0), Numerics.Max(end.Y, 0))); } } } catch (Exception ex) { MessageBox.Show(ex.Message, "There was a runtime error with your brush script."); } #endregion } #endregion } #endregion } lastPointOnRenderArea = pointOnRenderArea; lastPointOnRender = pointOnRender; }
private void Application_Idle(object sender, EventArgs e) { Vector2 pointOnRenderArea = Numerics.ToVector(renderArea.PointToClient(Control.MousePosition)); Vector2 pointOnRender = renderArea.ClientToImage(pointOnRenderArea); if (renderArea.Focused && Document.SelectedRender != null) { #region Undo if (undoing) { Undo(); } #endregion #region Redo if (redoing) { Redo(); } #endregion #region Panning if (MouseButtons == MouseButtons.Middle) { renderArea.ImageOffset += (pointOnRenderArea - lastPointOnRenderArea) / renderArea.ImageScale; } #endregion #region Brush //Select the appropriate brush based on the active mouse buttons. HeightBrush activeBrush = null; if (MouseButtons == MouseButtons.Left) { activeBrush = Document.LeftBrush; } else if (MouseButtons == MouseButtons.Right) { activeBrush = Document.RightBrush; } //If a mouse button was in use; paint. if (activeBrush != null) { HeightRender render = Document.SelectedRender; PhotonGradient gradient = Document.SelectedGradient; IEnumerable <HeightRender.Effect> effects; if (paintEffectsBox.Checked) { effects = Document.SelectedEffects; } else { effects = null; //If we aren't painting with effects then we don't care about supplying them to the update. } #region Process Steps //The brush stroke is broken up into steps that are each a given length (in pixels). The length is denoted by the brush precision. Vector2 brushDelta = lastPointOnRender - pointOnRender; float strokeLength = (float)brushDelta.Length; float steps = strokeLength / activeBrush.Precision + 1; Vector2 brushStep = brushDelta * (1.0f / steps); for (int i = (int)steps - 1; i >= 0; i--) //Work backwards so the undo and redo functionality makes sense. { //Calculate the current brush position, based on the starting point, the step vector and the current step index. Vector2 brushPosition = pointOnRender + brushStep * i; #region Process Brush Paint try { //Perform the actual brush operation and capture the selected area it affects. FieldSelection brushArea; float[] previousData; activeBrush.Paint(render.HeightField, (int)brushPosition.X, (int)brushPosition.Y, strokeLength, out brushArea, out previousData); //Add this paint event to the undo queue. undoQueue.Enqueue(new BrushAction(brushArea, previousData)); redoQueue.Clear(); //Break up the brush area into multiple parts if they intersect the edges of the image. foreach (FieldSelection fs in brushArea.SubSelectionsOf(render.HeightField)) { //Omit any parts that have a width or height of zero. if (!fs.IsEmpty) { //Update the corresponding part of the render. render.UpdateArea(fs.Left, fs.Top, fs.Width, fs.Height, gradient, effects); //Invalidate the corresponding part of the image panel so that it redraws itself in realtime. Point start = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Left, fs.Top))); Point end = Numerics.ToPoint(renderArea.ImageToClient(new Vector2(fs.Right, fs.Bottom))); renderArea.Invalidate(new Rectangle(Numerics.Max(start.X, 0), Numerics.Max(start.Y, 0), Numerics.Max(end.X, 0), Numerics.Max(end.Y, 0))); } } } catch (Exception ex) { //This usually happens when activeBrush.Paint is called, because it executes the user's brush script. Shows the script error message as a message box. MessageBox.Show(ex.Message, "There was a runtime error with your brush script."); } #endregion } #endregion } #endregion } else { undoing = redoing = false; } lastPointOnRenderArea = pointOnRenderArea; lastPointOnRender = pointOnRender; }