/// <summary> /// Initialize a new empty instance of the <c>Graphmatic.Interaction.Plotting.Graph</c> class from serialized XML data. /// </summary> /// <param name="xml">The XML data to use for deserializing this Resource.</param> public Graph(XElement xml) { UnresolvedResources = new Dictionary <Guid, PlottableParameters>(); Resources = new Dictionary <IPlottable, PlottableParameters>(); Parameters = new GraphParameters(xml.Element("GraphParameters")); XElement contents = xml.Element("Content"); foreach (XElement content in contents.Elements()) { XElement plottable = content.Elements() .Where(x => x.Name != "PlottableParameters") .First(); PlottableParameters parameters = new PlottableParameters(content.Element("PlottableParameters")); if (plottable.Name == "Reference") { UnresolvedResources.Add(Guid.Parse(plottable.Attribute("ID").Value), parameters); } else { IPlottable resource = plottable.Deserialize <IPlottable>(); Resources.Add(resource, parameters); if (resource is GraphKey) { Key = resource as GraphKey; } else if (resource is GraphAxis) { Axes = resource as GraphAxis; } } } }
/// <summary> /// Plots this IPlottable onto a given graph. The specific method of plotting will depend on the implementation of the /// IPlottable. /// </summary> /// <param name="graph">The Graph to plot this IPlottable onto.</param> /// <param name="graphics">The GDI+ drawing surface to use for plotting this IPlottable.</param> /// <param name="graphSize">The size of the Graph on the screen. This is a property of the display rather than the /// graph and is thus not included in the graph's parameters.</param> /// <param name="plotParams">The parameters used to plot this IPlottable.</param> /// <param name="resolution">The plotting resolution to use. Using a coarser resolution may make the plotting /// process faster, and is thus more suitable when the display is being resized or moved.</param> public void PlotOnto(Graph graph, Graphics g, Size graphSize, PlottableParameters plotParams, PlotResolution resolution) { int originX, originY; graph.ToImageSpace(graphSize, 0, 0, out originX, out originY); PlotGridLinesOnto(graph, g, graphSize, plotParams, originX, originY); PlotAxesOnto(g, graphSize, plotParams, originX, originY); }
/// <summary> /// Adds a resource to this Graph with the given drawing parameters. /// </summary> /// <param name="plottable">The <c>IPlottable</c> to add to the graph.</param> /// <param name="parameters">The parameters to use for plotting the object.</param> public void Add(IPlottable plottable, PlottableParameters parameters) { if (Resources.ContainsKey(plottable)) { throw new InvalidOperationException("The Graph already contains this IPlottable."); } else { Resources.Add(plottable, parameters); OnUpdate(); } }
/// <summary> /// Plots the main axis lines onto the graph, not including the grid lines. /// </summary> /// <param name="graphics">The GDI+ drawing surface to use for plotting this IPlottable.</param> /// <param name="graphSize">The size of the Graph on the screen. This is a property of the display rather than the /// graph and is thus not included in the graph's parameters.</param> /// <param name="plotParams">The parameters used to plot this IPlottable.</param> /// <param name="originX">The X position of the origin on the screen.</param> /// <param name="originY">The Y position of the origin on the screen.</param> private void PlotAxesOnto(Graphics graphics, Size graphSize, PlottableParameters plotParams, int originX, int originY) { using (Pen axisPen = new Pen(plotParams.PlotColor)) { if (originY >= 0 && originY < graphSize.Height) { graphics.DrawLine(axisPen, 0, originY, graphSize.Width, originY); } if (originX >= 0 && originX < graphSize.Width) { graphics.DrawLine(axisPen, originX, 0, originX, graphSize.Height); } } }
/// <summary> /// Plots this IPlottable onto a given graph. The specific method of plotting will depend on the implementation of the /// IPlottable. /// </summary> /// <param name="graph">The Graph to plot this IPlottable onto.</param> /// <param name="graphics">The GDI+ drawing surface to use for plotting this IPlottable.</param> /// <param name="graphSize">The size of the Graph on the screen. This is a property of the display rather than the /// graph and is thus not included in the graph's parameters.</param> /// <param name="plotParams">The parameters used to plot this IPlottable.</param> /// <param name="resolution">The plotting resolution to use. Using a coarser resolution may make the plotting /// process faster, and is thus more suitable when the display is being resized or moved.</param> public void PlotOnto(Graph graph, Graphics graphics, Size graphSize, PlottableParameters plotParams, PlotResolution resolution) { using (Brush brush = new SolidBrush(plotParams.PlotColor)) { float currentY = graphSize.Height; int offset = 5; foreach (IPlottable plot in graph.Reverse()) { if (plot is Resource) // only plot Resources, so we don't plot the key on the key { PlottableParameters parameters = graph[plot]; Resource resource = plot as Resource; string name = resource.Name; SizeF textSize = graphics.MeasureString(name, SystemFonts.DefaultFont); currentY -= textSize.Height + offset; float resourceX = graphSize.Width - offset - textSize.Width; graphics.DrawString(name, SystemFonts.DefaultFont, brush, resourceX, currentY); using (Pen resourcePen = new Pen(parameters.PlotColor)) // plot in the color of the resource { if (resource is DataSet) { // draw a cross for a data set resourcePen.Width = DataSet.DataPointPenWidth; graphics.DrawLine(resourcePen, new PointF(resourceX - DataSet.DataPointCrossSize - 5, currentY - DataSet.DataPointCrossSize + textSize.Height / 2), new PointF(resourceX + DataSet.DataPointCrossSize - 5, currentY + DataSet.DataPointCrossSize + textSize.Height / 2)); graphics.DrawLine(resourcePen, new PointF(resourceX - DataSet.DataPointCrossSize - 5, currentY + DataSet.DataPointCrossSize + textSize.Height / 2), new PointF(resourceX + DataSet.DataPointCrossSize - 5, currentY - DataSet.DataPointCrossSize + textSize.Height / 2)); } else if (resource is Equation) { // draw a line for an equation resourcePen.Width = Equation.EquationPenWidth; graphics.DrawLine(resourcePen, new PointF(resourceX - 2, currentY + textSize.Height / 2), new PointF(resourceX - 15, currentY + textSize.Height / 2)); } } } } } }
/// <summary> /// Plots grid lines onto the graph. /// </summary> /// <param name="graphics">The GDI+ drawing surface to use for plotting this IPlottable.</param> /// <param name="graphSize">The size of the Graph on the screen. This is a property of the display rather than the /// graph and is thus not included in the graph's parameters.</param> /// <param name="plotParams">The parameters used to plot this IPlottable.</param> /// <param name="originX">The X position of the origin on the screen.</param> /// <param name="originY">The Y position of the origin on the screen.</param> private void PlotGridLinesOnto(Graph graph, Graphics graphics, Size graphSize, PlottableParameters plotParams, int originX, int originY) { using (Pen majorPen = new Pen(plotParams.PlotColor.ColorAlpha(0.25))) using (Pen minorPen = new Pen(plotParams.PlotColor.ColorAlpha(0.15))) using (Brush valueBrush = new SolidBrush(plotParams.PlotColor.ColorAlpha(0.5))) { // the increment, in pixels, of each grid space double incrementX = HorizontalType.AxisTypeGridScale() * GridSize / graph.Parameters.HorizontalPixelScale, incrementY = VerticalType.AxisTypeGridScale() * GridSize / graph.Parameters.VerticalPixelScale; // draw the variable names onto the axis graphics.DrawString(graph.Parameters.VerticalAxis.ToString(), SystemFonts.DefaultFont, valueBrush, (int)originX, 2); graphics.DrawString(graph.Parameters.HorizontalAxis.ToString(), SystemFonts.DefaultFont, valueBrush, (int)graphSize.Width - 16, (int)originY); // the unit suffix of the grid labels, for example the little circle for degrees string horizontalAxisSuffix = HorizontalType.AxisTypeExtension(); string verticalAxisSuffix = VerticalType.AxisTypeExtension(); // the axis label scale for the numbers along each axis // for example, axes plotted in degree mode are scaled such that 2*pi is displayed // as 360 degrees instead double horizontalAxisScale = GridSize * HorizontalType.AxisTypeLabelScale() * HorizontalType.AxisTypeGridScale(); double verticalAxisScale = GridSize * VerticalType.AxisTypeLabelScale() * VerticalType.AxisTypeGridScale(); // the remainder of the code in this method plots the gridlines from the origin outward. // this is potentially not the most efficient method of doing it, but it avoids floating- // point rounding errors resulting in axis labels like 2.00000000000017 without adding in // even more horrible code. // some of this code looks redundant, and it is. However as drawing the grid lines is a // (surprisingly) CPU intensive operation this method needs to be as fast as possible as // the grid is drawn every time the graph is double value = originX; int index = 0; // plot horizontal grid lines from the origin to the right of the page while (value < graphSize.Width) { bool major = index % MajorInterval == 0; if (major) { graphics.DrawLine(majorPen, (int)value, 0, (int)value, graphSize.Height); graphics.DrawString( String.Format("{0:0.####}{1}", index * horizontalAxisScale, horizontalAxisSuffix), SystemFonts.DefaultFont, valueBrush, (int)value, (int)originY); } else { graphics.DrawLine(minorPen, (int)value, 0, (int)value, graphSize.Height); } value += incrementX; index++; } value = originX; index = 0; // plot hoz grid from origin to left of page while (value >= 0) { bool major = index % MajorInterval == 0; if (major) { graphics.DrawLine(majorPen, (int)value, 0, (int)value, graphSize.Height); graphics.DrawString( String.Format("{0:0.####}{1}", index * horizontalAxisScale, horizontalAxisSuffix), SystemFonts.DefaultFont, valueBrush, (int)value, (int)originY); } else { graphics.DrawLine(minorPen, (int)value, 0, (int)value, graphSize.Height); } value -= incrementX; index++; } value = originY; index = 0; // plot vertical grid lines from origin to bottom of page while (value < graphSize.Height) { bool major = index % MajorInterval == 0; if (major) { graphics.DrawLine(majorPen, 0, (int)value, graphSize.Width, (int)value); graphics.DrawString( String.Format("{0:0.####}{1}", index * verticalAxisScale, verticalAxisSuffix), SystemFonts.DefaultFont, valueBrush, (int)originX, (int)value); } else { graphics.DrawLine(minorPen, 0, (int)value, graphSize.Width, (int)value); } value += incrementY; index++; } value = originY; index = 0; // plot vert grid from origin to top of page while (value >= 0) { bool major = index % MajorInterval == 0; if (major) { graphics.DrawLine(majorPen, 0, (int)value, graphSize.Width, (int)value); double vertical = graph.Parameters.VerticalPixelScale * index; graphics.DrawString( String.Format("{0:0.####}{1}", index * verticalAxisScale, verticalAxisSuffix), SystemFonts.DefaultFont, valueBrush, (int)originX, (int)value); } else { graphics.DrawLine(minorPen, 0, (int)value, graphSize.Width, (int)value); } value -= incrementY; index++; } } }