Imports MGraphing.GraphItems
Imports System.Text.RegularExpressions
Imports System.CodeDom.Compiler
'Written by Totem
Public Class GraphArea
Private Structure MethodInfoPlus
Dim Method As Reflection.MethodInfo
Dim Target As Object
End Structure
Private _Items As List(Of GraphItemBase)
Private EssentialItems As List(Of GraphItemBase)
Private _Center As Point
Private _AxisColor, _FunctionColor, _GridLinesColor As Color
Private _PixelsPerDot As Int32
Private _StartInZero, _AccurateLine, _ShowGridLines As Boolean
Private _DrawValues, _LaunchExceptionOnDrawError As Boolean
Private _DrawValueIncrement As Single
Private EvaluateX As Boolean = True
'Queste servono per salvare l'immagine in seguito
Private AssociatedImage As Bitmap
Private AssociatedGraphic As Graphics = Nothing
Sub New()
Me.InitializeComponent()
Me.AxisColor = Color.Black
Me.FunctionColor = Color.Red
Me.Center = New Point(Me.Width / 3, Me.Height / 3)
Me.PixelsPerDot = 5
Me.StartInZero = False
Me.AccurateLine = False
Me.DrawValues = False
Me._DrawValueIncrement = 10
Me.LaunchExceptionOnDrawError = True
Me.ShowGridLines = False
_Items = New List(Of GraphItemBase)
EssentialItems = New List(Of GraphItemBase)
End Sub
''' <summary>
''' Tutti gli elementi da disegnare nel grafico.
''' </summary>
Public ReadOnly Property Items() As List(Of GraphItemBase)
Get
Return _Items
End Get
End Property
''' <summary>
''' Le coordinate del centro degli assi cartesiani.
''' </summary>
Public Property Center() As Point
Get
Return _Center
End Get
Set(ByVal Value As Point)
_Center = Value
End Set
End Property
''' <summary>
''' Il colore con cui disegnare gli assi cartesiani.
''' </summary>
Public Property AxisColor() As Color
Get
Return _AxisColor
End Get
Set(ByVal Value As Color)
_AxisColor = Value
End Set
End Property
''' <summary>
''' Il colore con cui disegnare la funzione.
''' </summary>
Public Property FunctionColor() As Color
Get
Return _FunctionColor
End Get
Set(ByVal Value As Color)
_FunctionColor = Value
End Set
End Property
''' <summary>
''' Il colore con cui disegnare la griglia del grafico.
''' </summary>
Public Property GridLinesColor() As Color
Get
Return _GridLinesColor
End Get
Set(ByVal Value As Color)
_GridLinesColor = Value
End Set
End Property
''' <summary>
''' Determina quanti pixel cosituiscono un punto.
''' </summary>
Public Property PixelsPerDot() As Int32
Get
Return _PixelsPerDot
End Get
Set(ByVal Value As Int32)
_PixelsPerDot = Value
End Set
End Property
''' <summary>
''' Determina se la funzione inizia in (0;0) automaticamente.
''' </summary>
Public Property StartInZero() As Boolean
Get
Return _StartInZero
End Get
Set(ByVal Value As Boolean)
_StartInZero = Value
End Set
End Property
''' <summary>
''' Determina se conservare le coordinate decimali fino all'ultimo per poi convertirle in interi. In questo
''' modo viene ridotto l'errore di approssimazione.
''' </summary>
Public Property AccurateLine() As Boolean
Get
Return _AccurateLine
End Get
Set(ByVal Value As Boolean)
_AccurateLine = Value
End Set
End Property
''' <summary>
''' Determina se disegnare anche i valori sugli assi.
''' </summary>
Public Property DrawValues() As Boolean
Get
Return _DrawValues
End Get
Set(ByVal Value As Boolean)
_DrawValues = Value
End Set
End Property
''' <summary>
''' Determina ogni quanto disegnare i valori sugli assi.
''' </summary>
Public Property DrawValueIncrement() As Single
Get
Return _DrawValueIncrement
End Get
Set(ByVal Value As Single)
If DrawValues Or ShowGridLines Then
If Value > 0 Then
_DrawValueIncrement = Value
Else
_DrawValueIncrement = 10
End If
End If
End Set
End Property
''' <summary>
''' Determina se viene visualizzato un messaggio di errore quando, nella fase di disegno, vengono
''' passati parametri scorretti alle funzioni oppure si va in overflow.
''' </summary>
Public Property LaunchExceptionOnDrawError() As Boolean
Get
Return _LaunchExceptionOnDrawError
End Get
Set(ByVal Value As Boolean)
_LaunchExceptionOnDrawError = Value
End Set
End Property
''' <summary>
''' Determina se disegnare anche la griglia del grafico.
''' </summary>
Public Property ShowGridLines() As Boolean
Get
Return _ShowGridLines
End Get
Set(ByVal Value As Boolean)
_ShowGridLines = Value
End Set
End Property
Private Function GetValueOnX(ByVal Value As Single) As GraphItemBase
Dim Position As New Point(Center.X + (Value * Me.PixelsPerDot), Center.Y)
Dim ValueName As New GraphString(Position, Value.ToString, Me.Font)
Return ValueName
End Function
Private Function GetValueOnY(ByVal Value As Single) As GraphItemBase
Dim Position As New Point(Center.X, Center.Y - (Value * Me.PixelsPerDot))
Dim ValueName As New GraphString(Position, Value.ToString, Me.Font)
Return ValueName
End Function
'Gran parte della funzione che segue e' stata presa da un libro
'sulla programmazione in VB2005 di Francesco Balena, tuttavia ho apportato alcune
'modifiche per adattarla al mio codice
Private Function CreateEvaluator(ByVal Expression As String) As MethodInfoPlus
Dim RealExpression As String = Expression
Dim FunctionX As New Regex("(?<function>\w)\s*=\s*")
Dim M As Match = FunctionX.Match(Expression)
If M.Success Then
If M.Groups("function").Value.ToLower = "y" Then
EvaluateX = True
Else
EvaluateX = False
End If
Else
Throw New ArgumentException("Espressione non valida!")
End If
If Expression.IndexOf("=") > -1 Then
RealExpression = Expression.Remove(0, Expression.IndexOf("=") + 1)
End If
Dim Code As String = _
"Imports Microsoft.VisualBasic" & vbCrLf & _
"Imports System" & vbCrLf & _
"Imports System.Math" & vbCrLf & _
"Public Class Evaluator" & vbCrLf & _
" Public Function Evaluate(ByVal X As Single) As Single" & vbCrLf & _
" Return " & RealExpression & vbCrLf & _
" End Function" & vbCrLf & _
"End Class" & vbCrLf
If Not EvaluateX Then
Code = Code.Replace("X", "Y")
End If
Dim Parameters As New CodeDom.Compiler.CompilerParameters
With Parameters
.GenerateExecutable = False
.IncludeDebugInformation = True
.TempFiles.KeepFiles = True
.GenerateInMemory = False
#Else
.TreatWarningsAsErrors = True
.TempFiles.KeepFiles = False
.GenerateInMemory = True
#End If
.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")
.ReferencedAssemblies.Add("System.dll")
End With
Dim Provider As New VBCodeProvider
Dim CompResults As CompilerResults = Provider.CompileAssemblyFromSource(Parameters, Code)
If CompResults.Errors.Count > 0 Then
Dim Msg As String = ""
For Each Err As CompilerError In CompResults.
Errors
Msg
&= Err.
ToString & vbCrLf
Next
'MsgBox(Msg, MsgBoxStyle.Critical)
Throw New ArgumentException("Espressione non valida!")
Else
Dim Asm As Reflection.Assembly = CompResults.CompiledAssembly
Dim Evaluator As Object = Asm.CreateInstance("Evaluator")
Dim EvalMethod As Reflection.MethodInfo = Evaluator.GetType.GetMethod("Evaluate")
Dim Result As MethodInfoPlus
Result.Method = EvalMethod
Result.Target = Evaluator
Return Result
End If
End Function
Private Function Evaluate(ByVal Method As MethodInfoPlus, ByVal Value As Single) As Single
Dim Args() As Object = {Value}
Dim Result As Object = Method.Method.Invoke(Method.Target, Args)
Return CSng(Result)
End Function
Public Sub DrawFunction(ByVal Expression As String, ByVal Min As Single, ByVal Max As Single, ByVal Increment As Single)
Dim X, Y As Single
Dim OldX, OldY As Single
Dim Item As GraphLine
Dim ExactItem As ExactGraphLine
Dim Evaluator As MethodInfoPlus = CreateEvaluator(Expression)
Me.Items.Clear()
For Value As Single = Min To Max Step Increment
OldX = X
OldY = Y
If EvaluateX Then
X = Value
Y = Evaluate(Evaluator, X)
Else
Y = Value
X = Evaluate(Evaluator, Y)
End If
If (Not StartInZero) And (OldX = 0 And OldY = 0) And Value = Min Then
OldX = X
OldY = Y
End If
If Me.AccurateLine Then
ExactItem = New ExactGraphLine(OldX, OldY, X, Y)
ExactItem.Color = Me.FunctionColor
ExactItem.ToPixel(Me.PixelsPerDot)
ExactItem.SetAbsolutePosition(Me.Center)
ExactItem.SetAbsoluteEndPoint(Me.Center)
Me.Items.Add(ExactItem)
Else
Item = New GraphLine(New Point(OldX, OldY), New Point(X, Y))
Item.Color = Me.FunctionColor
Item.ToPixel(Me.PixelsPerDot)
Item.Position = Item.GetAbsolutePosition(Me.Center)
Item.EndPoint = Item.GetAbsoluteEndPoint(Me.Center)
Me.Items.Add(Item)
End If
Next
Me.Refresh()
End Sub
Public Sub Save()
Dim FSave As New SaveFileDialog
FSave.Filter = "File Bitmap|*.bmp"
If FSave.ShowDialog = DialogResult.OK Then
AssociatedGraphic = Graphics.FromImage(AssociatedImage)
AssociatedImage.Save(FSave.FileName)
End If
End Sub
Public Sub RedrawEssentialItems()
Dim Line As GraphLine
Dim Value As GraphString
EssentialItems.Clear()
With EssentialItems
If Me.DrawValueIncrement = 0 Then
MessageBox.Show("DrawValueIncrement ha valore 0! Il rendering è stato interrotto poichè tale valore avrebbe portato " & _
"l'applicazione ad un crash.", "MGraphing", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Exit Sub
End If
'Valori
If Me.DrawValues Then
For X As Single = 0 To (Me.Width / Me.PixelsPerDot) Step Me.DrawValueIncrement
Value = GetValueOnX(X)
.Add(Value)
Value = GetValueOnX(-X)
.Add(Value)
Next
For Y As Single = 0 To (Me.Height / Me.PixelsPerDot) Step Me.DrawValueIncrement
Value = GetValueOnY(Y)
.Add(Value)
Value = GetValueOnY(-Y)
.Add(Value)
Next
End If
'MsgBox(Me.ShowGridLines & " " & Me.DrawValueIncrement)
'Griglia
If Me.ShowGridLines Then
For X As Single = 0 To (Me.Width / Me.PixelsPerDot) Step Me.DrawValueIncrement
Line = New GraphLine(New Point(Center.X + (X * Me.PixelsPerDot), 0), New Point(Center.X + (X * Me.PixelsPerDot), Me.Height))
Line.Color = Me.GridLinesColor
.Add(Line)
Line = New GraphLine(New Point(Center.X - (X * Me.PixelsPerDot), 0), New Point(Center.X - (X * Me.PixelsPerDot), Me.Height))
Line.Color = Me.GridLinesColor
.Add(Line)
Next
For Y As Single = 0 To (Me.Height / Me.PixelsPerDot) Step Me.DrawValueIncrement
Line = New GraphLine(New Point(0, Center.Y - (Y * Me.PixelsPerDot)), New Point(Me.Width, Center.Y - (Y * Me.PixelsPerDot)))
Line.Color = Me.GridLinesColor
.Add(Line)
Line = New GraphLine(New Point(0, Center.Y + (Y * Me.PixelsPerDot)), New Point(Me.Width, Center.Y + (Y * Me.PixelsPerDot)))
Line.Color = Me.GridLinesColor
.Add(Line)
Next
End If
'Asse X
Line = New GraphLine(New Point(0, Center.Y), New Point(Me.Width, Center.Y))
Line.Color = Me.AxisColor
.Add(Line)
'Asse Y
Line = New GraphLine(New Point(Center.X, 0), New Point(Center.X, Me.Height))
Line.Color = Me.AxisColor
.Add(Line)
End With
End Sub
Public Overrides Sub Refresh()
RedrawEssentialItems()
MyBase.Refresh()
End Sub
'Da perfezionare...
Public Sub DoAxialSymmetry(ByVal Parameter As Char, ByVal Constant As Single)
If Parameter = "x" Then
Dim Line As GraphLine
Dim ELine As ExactGraphLine
Dim Temp As New List(Of GraphItemBase)
For Each GenericItem As GraphItemBase In Me.Items
If (TypeOf GenericItem Is GraphLine) Then
Line = CType(GenericItem, GraphLine).Clone
Line.Position = New Point(2 * Constant - Line.Position.X, Line.Position.Y)
Line.EndPoint = New Point(2 * Constant - Line.EndPoint.X, Line.Position.Y)
Temp.Add(Line)
ElseIf (TypeOf GenericItem Is ExactGraphLine) Then
ELine = CType(GenericItem, ExactGraphLine)
ELine.StartX = 2 * Constant - ELine.StartX
ELine.EndX = 2 * Constant - ELine.EndX
Temp.Add(ELine)
End If
Next
Me.Items.AddRange(Temp)
ElseIf Parameter = "y" Then
Dim Line As GraphLine
Dim ELine As ExactGraphLine
Dim Temp As New List(Of GraphItemBase)
For Each GenericItem As GraphItemBase In Me.Items
If (TypeOf GenericItem Is GraphLine) Then
Line = CType(GenericItem, GraphLine).Clone
Line.Position = New Point(Line.Position.X, 2 * Constant - Line.Position.Y)
Line.EndPoint = New Point(Line.EndPoint.X, 2 * Constant - Line.Position.Y)
Temp.Add(Line)
ElseIf (TypeOf GenericItem Is ExactGraphLine) Then
ELine = CType(GenericItem, ExactGraphLine).Clone
ELine.StartY = 2 * Constant - ELine.StartY
ELine.EndY = 2 * Constant - ELine.EndY
Temp.Add(ELine)
End If
Next
Me.Items.AddRange(Temp)
End If
End Sub
Public Sub CompleteSymmetricRelation()
Dim First, Last As Point
Dim GenericItem As GraphItemBase
If Me.Items.Count = 0 Then
Exit Sub
End If
GenericItem = Me.Items(Me.Items.Count - 1)
If (TypeOf GenericItem Is GraphLine) Then
Dim Line As Object = GenericItem
Last = Line.EndPoint
End If
GenericItem = Me.Items(0)
If (TypeOf GenericItem Is GraphLine) Or (TypeOf GenericItem Is ExactGraphLine) Then
Dim Line As Object = GenericItem
First = Line.Position
End If
If First.Y > Last.Y Then
DoAxialSymmetry("y", First.Y)
Else
DoAxialSymmetry("y", Last.Y)
End If
If First.X > Last.X Then
DoAxialSymmetry("x", Last.X)
Else
DoAxialSymmetry("x", First.X)
End If
MyBase.Refresh()
End Sub
'---------------------------
Private Sub GraphArea_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
RedrawEssentialItems()
AssociatedImage = New Bitmap(Me.Width, Me.Height)
'AssociatedGraphic = Graphics.FromImage(AssociatedImage)
End Sub
Private Sub GraphArea_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
If AssociatedGraphic Is Nothing Then
AssociatedGraphic = Graphics.FromImage(AssociatedImage)
End If
AssociatedGraphic.Clear(Me.BackColor)
For Each Item As GraphItemBase In EssentialItems
Item.Draw(e.Graphics)
Item.Draw(AssociatedGraphic)
Next
For Each Item As GraphItemBase In Items
Try
Item.Draw(e.Graphics)
Item.Draw(AssociatedGraphic)
Catch Ex As Exception
If Me.LaunchExceptionOnDrawError Then
MessageBox.Show("Si e' verificato un errore durante le operazioni di disegno. La causa puo' essere imputata a valori troppo elevati delle coordinate dei punti, " & _
"oppure a valori non rappresentabili, quali Infinito, -Infinito o Nessun Valore.", "MGraphing", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End If
End Try
Next
End Sub
Private Sub GraphArea_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
If AssociatedImage IsNot Nothing Then
AssociatedImage = New Bitmap(AssociatedImage, Me.Width, Me.Height)
AssociatedGraphic = Graphics.FromImage(AssociatedImage)
End If
End Sub
End Class