///
/// This file is part of ILNumerics Community Edition.
///
/// ILNumerics Community Edition - high performance computing for applications.
/// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
///
/// ILNumerics Community Edition is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License version 3 as published by
/// the Free Software Foundation.
///
/// ILNumerics Community Edition is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with ILNumerics Community Edition. See the file License.txt in the root
/// of your distribution package. If not, see .
///
/// In addition this software uses the following components and/or licenses:
///
/// =================================================================================
/// The Open Toolkit Library License
///
/// Copyright (c) 2006 - 2009 the Open Toolkit library.
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights to
/// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
/// the Software, and to permit persons to whom the Software is furnished to do
/// so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// =================================================================================
///
#pragma warning disable 1591
using System;
using System.Collections.Generic;
using System.Text;
using ILNumerics.Exceptions;
using ILNumerics.Drawing.Misc;
using ILNumerics.Drawing.Interfaces;
using ILNumerics.Drawing.Graphs;
using ILNumerics.Drawing.Plots;
namespace ILNumerics.Drawing.Collections {
///
/// Collection of graph objects - all graphs currently contained in a subfigure
///
public sealed class ILGraphCollection : List, IDisposable {
public event ILGraphCollectionChangedEvent CollectionChanged;
public event ILGraphChangedEvent GraphChanged;
///
/// triggers the ILGraphCollectionChanged event
///
///
///
void OnChange (ILGraph graph, GraphCollectionChangeReason reason, IILPanelConfigurator configurator) {
if (CollectionChanged != null)
CollectionChanged(this, new ILGraphCollectionChangedEventArgs((ILGraph)graph, reason, configurator));
}
///
/// triggers GraphChanged event, bubbles from single graph
///
/// event arguments from graph
void OnGraphChanged(ILGraph sender, ILGraphChangedEventArgs graphArgs) {
if (GraphChanged != null) {
GraphChanged(sender,graphArgs);
}
}
#region members / properties
private IILCreationFactory m_graphFact;
private ILClippingData m_clippingData;
private List m_unsortedCache;
private ILGraphComparer m_graphComparer;
///
/// Clippping volume for data in all graphs of the collection
///
/// This gives back the real ILClippingData object (no copy)
public ILClippingData Limits {
get {
return m_clippingData;
}
}
#endregion
#region constructor / disposal
///
/// Create new ILGraphCollection
///
/// Output panel
///
internal ILGraphCollection(IILCreationFactory vPainterFact) : base() {
m_graphFact = vPainterFact;
m_clippingData = new ILClippingData();
m_unsortedCache = new List();
m_graphComparer = new ILGraphComparer();
}
///
/// dispose all graphs contained in this collection
///
public void Dispose() {
foreach (ILGraph graph in this) {
if (graph != null)
graph.Dispose();
}
}
#endregion
#region event handling
/////
///// signaled when one/some of the ILGraphs have changed
/////
/////
/////
//void Graph_Changed(object sender, ILGraphChangedEventArgs e) {
// OnGraphChanged(sender as ILGraph,e);
//}
///
/// called once the limits of a graph have changed
///
/// graph
/// event args, holding a reference to the clipping data
void Limits_Changed(object sender, ClippingChangedEventArgs e) {
ILPoint3Df max = ILPoint3Df.MinValue;
ILPoint3Df min = ILPoint3Df.MaxValue;
foreach (ILGraph graph in this) {
max = ILPoint3Df.Max(max,graph.Limits.Max);
min = ILPoint3Df.Min(min,graph.Limits.Min);
}
m_clippingData.Set(min,max);
}
#endregion
#region Collection manager
///
/// clear all graphs from the collection
///
public new void Clear() {
lock (this) {
foreach (ILGraph g in this) {
g.Dispose();
}
base.Clear();
}
m_clippingData.Reset();
Invalidate();
OnChange(null,GraphCollectionChangeReason.Deleted,null);
}
///
/// Add a new surface graph, provide all coordinate data
///
/// matrix holding Z coordinates (heights)
/// reference to newly created surface graph
public ILSurfaceGraph AddSurfGraph(ILInArray X, ILInArray Y, ILInArray Z, ILInArray C) {
using (ILScope.Enter(X, Y, Z, C)) {
ILSurfaceGraph ret;
ret = (ILSurfaceGraph)m_graphFact.CreateGraph(Z, GraphType.Surf, X, Y, C);
m_clippingData.Update(ret.Limits);
Add(ret);
ret.Changed += new ILGraphChangedEvent(GraphChanged);
// trigger change event
OnChange(ret, GraphCollectionChangeReason.Added, null);
return ret;
}
}
///
/// Add a new surface graph to collection
///
/// matrix holding data to be plotted
/// reference to newly created surface graph
public ILSurfaceGraph AddSurfGraph(ILInArray data) {
using (ILScope.Enter(data)) {
return (ILSurfaceGraph)Add(data, GraphType.Surf)[0];
}
}
///
/// add a plot to a new scene graph
///
/// plot to be added to the panel
/// newly created scene graph
public ILSceneGraph AddPlot(ILPlot plot) {
ILSceneGraph scene = AddSceneGraph();
scene.AddNode(plot);
return scene;
}
///
/// Add a new scene graph to collection
///
/// reference to newly created surface graph
public ILSceneGraph AddSceneGraph() {
ILSceneGraph newGraph = m_graphFact.CreateSceneGraph();
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
newGraph.Limits.Changed += new ILClippingDataChangedEvent(Limits_Changed);
newGraph.NodeAdded += new SceneGraphNodeHandler(sceneGraph_NodeAdded);
OnChange(newGraph, GraphCollectionChangeReason.Added, null);
return newGraph;
}
void sceneGraph_NodeAdded(object sender, ILSceneGraphNodeEventArgs args) {
if (args.Node != null && args.Node is IILPanelConfigurator) {
OnChange(sender as ILGraph, GraphCollectionChangeReason.Added, args.Node as IILPanelConfigurator);
}
}
///
/// add an newly created scene graph instance to the collection of graphs
///
/// exisisting instance of scene graph
/// This overload may be used to add user defined graphs to
/// the collection of graphs in order to use them in ILPanels. The user
/// defined graph needs to derive from scene graph. It may uses all shapes
/// compatible with ILSceneGraph. (ILLine,ILPolygon,ILLitBox,ILQuads,ILTriangles, etc...)
///
public void AddSceneGraph(ILSceneGraph sceneGraph) {
if (sceneGraph == null) throw new ILArgumentException("scene graph must not be null!");
Add(sceneGraph);
sceneGraph.Changed += new ILGraphChangedEvent(GraphChanged);
sceneGraph.Limits.Changed += new ILClippingDataChangedEvent(Limits_Changed);
OnChange(sceneGraph, GraphCollectionChangeReason.Added, null);
}
///
/// Add a new imagesc graph to collection
///
/// matrix holding data to be drawn
/// newly created graph
public ILImageSCGraph AddImageSCGraph(ILInArray data) {
using (ILScope.Enter(data))
return (ILImageSCGraph)Add(data,GraphType.Imagesc)[0];
}
///
/// Add line graph(s)
///
/// vector or matrix with date to be plotted. For matrices, the columns will be used to create new graphs.
/// Array of newly created graph(s)
public ILPlot2DGraph[] AddLineGraph(ILInArray A) {
using (ILScope.Enter(A)) {
return AddLineGraph(ILMath.tosingle(A));
}
}
///
/// Add line graph(s)
///
/// vector or matrix with date to be plotted. For matrices, the columns will be used to create new graphs.
/// Array of newly created graph(s)
public ILPlot2DGraph[] AddLineGraph(ILInArray A) {
using (ILScope.Enter(A)) {
List graphs = Add(A, GraphType.Plot2D);
List ret = new List();
ILColorEnumerator cenumerator = new ILColorEnumerator(Colormaps.Lines);
foreach (ILGraph g in graphs) {
ILPlot2DGraph plot2DGraph = (ILPlot2DGraph)g;
ret.Add(plot2DGraph);
plot2DGraph.Line.Color = cenumerator.NextColor();
}
return ret.ToArray();
}
}
///
/// Add X/Y line graph(s)
///
/// X coordinates. If this is a matrix, every column provides data for an individual graph.
/// Y coordinates. Same size as 'X'.
/// newly created graph(s)
public ILPlot2DGraph[] AddLineGraph(ILInArray XData, ILInArray YData) {
using (ILScope.Enter(XData,YData)) {
return AddLineGraph(ILMath.tosingle(XData), ILMath.tosingle(YData));
}
}
///
/// Add X/Y line graph(s)
///
/// X coordinates. If this is a matrix, every column provides data for an individual graph.
/// Y coordinates. Same size as 'X'.
/// newly created graph(s)
public ILPlot2DGraph[] AddLineGraph(ILInArray XData, ILInArray YData) {
using (ILScope.Enter(XData, YData)) {
List graphs = Add(XData, YData, GraphType.Plot2D);
List ret = new List();
ILColorEnumerator cenumerator = new ILColorEnumerator(Colormaps.Lines);
foreach (ILGraph g in graphs) {
ILPlot2DGraph plot2DGraph = (ILPlot2DGraph)g;
ret.Add(plot2DGraph);
plot2DGraph.Line.Color = cenumerator.NextColor();
}
return ret.ToArray();
}
}
///
/// [Depricated] Add new line graph(s) (2D) to collection
///
/// vector/array with date to be plotted. For arrays, the columns will be used.
/// Array of newly created graph(s)
public ILPlot2DGraph[] AddPlot2DGraph(ILInArray data) {
return AddLineGraph(data);
}
///
/// [Depricated] Add new X/Y 2D line graph(s), provide X and Y coordinates
///
/// X coordinates. If this is a matrix, every column will produce an individual graph.
/// Y coordinates. Same size than 'X'.
/// Array of newly created graph(s)
public ILPlot2DGraph[] AddPlot2DGraph(ILInArray XData, ILInArray YData) {
return AddLineGraph(XData, YData);
}
///
/// Add new numeric graph(s) of arbitrary (math) type
///
/// data to be plotted
/// determine GraphType
/// List with newly created graph(s)
public List Add(ILInArray data, GraphType graphType) {
using (ILScope.Enter(data)) {
if (!data.IsNumeric)
throw new ILArgumentException("Add graph: data array must be numeric!");
List ret = new List();
ILGraph newGraph;
ILArray tmpData;
lock (this) {
#region add each graph type seperately
switch (graphType) {
case GraphType.Plot2D:
if (data.IsVector || data.IsScalar) {
newGraph = m_graphFact.CreateGraph(data, graphType);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
m_clippingData.Update(newGraph.Limits);
newGraph.Limits.Changed += new ILClippingDataChangedEvent(Limits_Changed);
ret.Add(newGraph);
} else if (data.IsMatrix) {
// plot columns
m_clippingData.EventingSuspend();
tmpData = ILMath.tosingle(data);
for (int c = 0; c < tmpData.Size[1]; c++) {
newGraph = m_graphFact.CreateGraph(tmpData[":", c], graphType);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
ret.Add(newGraph);
m_clippingData.Update(newGraph.Limits);
newGraph.Limits.Changed += new ILClippingDataChangedEvent(Limits_Changed);
}
m_clippingData.EventingResume();
}
// trigger change event
OnChange(ret[0], GraphCollectionChangeReason.Added, ret[0] as IILPanelConfigurator);
break;
case GraphType.Surf:
if (!data.IsMatrix)
throw new ILArgumentException("Surf: source data must be a matrix!");
tmpData = ILMath.tosingle(data);
m_clippingData.EventingSuspend();
// margin for Z-Direction
float min, max;
tmpData.GetLimits(out min, out max);
float zmarg = (max - min > 0) ? (float)(Math.Abs(max - min) / 10.0) : 0.0f;
m_clippingData.Update(new ILPoint3Df(0, 0, min - zmarg), new ILPoint3Df(data.Size[1] - 1, data.Size[0] - 1, max + zmarg));
m_clippingData.EventingResume();
newGraph = m_graphFact.CreateGraph(tmpData, graphType);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
// trigger change event
ret.Add(newGraph);
OnChange(newGraph, GraphCollectionChangeReason.Added, newGraph as IILPanelConfigurator);
break;
case GraphType.Imagesc:
if (!data.IsMatrix)
throw new ILArgumentException("Surf: source data must be a matrix!");
tmpData = ILMath.tosingle(data);
// note, ImageSC does not contribute to global clipping, except that the clipping
// will be updated to include z = 0!
m_clippingData.EventingSuspend();
m_clippingData.Update(new ILPoint3Df(-0.5f, -.5f, 0), new ILPoint3Df(data.Size[1] - 0.5f, data.Size[0] - 0.5f, 0));
m_clippingData.EventingResume();
newGraph = m_graphFact.CreateGraph(tmpData, graphType);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
// trigger change event
OnChange(newGraph, GraphCollectionChangeReason.Added, newGraph as IILPanelConfigurator);
ret.Add(newGraph);
break;
default:
throw new ILDrawingException("the type of drawing is not supported!");
}
#endregion
}
return ret;
}
}
///
/// Add new graph(s) of arbitrary type, provide both axis data
///
/// x coordinates
/// type of graph to be added
/// y coordinates
/// List with newly created graph(s)
/// The return value will be a list of all graphs created (and added),
/// since more than one graph may have been specified. This depends on the
/// shape of the data provided.
/// Currently only Plot2D graphs are supported as GraphType!
/// if the data provided are nor
/// numeric or the size for one of the arrays or
/// do not match.
public List Add(ILInArray xData, ILInArray yData, GraphType graphType) {
using (ILScope.Enter(xData, yData)) {
if (!yData.IsNumeric || !xData.IsNumeric)
throw new ILArgumentException("Add graph: data arrays must be numeric!");
List ret = new List();
ILGraph newGraph;
ILArray tmpDataY, tmpDataX;
lock (this) {
#region add each graph type seperately
switch (graphType) {
case GraphType.Plot2D:
if (!yData.Size.IsSameSize(xData.Size))
throw new ILArgumentException("Add graph: for X/Y plots, the size of X and Y must be equal!");
if (yData.IsVector || yData.IsScalar) {
newGraph = m_graphFact.CreateGraph(yData, graphType, xData);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
m_clippingData.Update(newGraph.Limits);
ret.Add(newGraph);
} else if (yData.IsMatrix) {
// plot columns
m_clippingData.EventingSuspend();
tmpDataY = ILMath.tosingle(yData);
tmpDataX = ILMath.tosingle(xData);
for (int c = 0; c < tmpDataY.Size[1]; c++) {
newGraph = m_graphFact.CreateGraph(tmpDataY[":", c], graphType, tmpDataX[":", c]);
Add(newGraph);
newGraph.Changed += new ILGraphChangedEvent(GraphChanged);
ret.Add(newGraph);
m_clippingData.Update(newGraph.Limits);
}
m_clippingData.EventingResume();
}
// trigger change event
OnChange(ret[0], GraphCollectionChangeReason.Added, ret[0] as IILPanelConfigurator);
break;
default:
throw new ILDrawingException("graph type is not supported in that mode yet!");
}
#endregion
}
return ret;
}
}
///
/// Remove a graph from the collection and rescale data limits
///
/// key of graph to be removed
/// the ILGraph removed or null, if the key does not exist
/// If the graph has been removed, the clipping data will be updated and a change event will be fired.
public new bool Remove(ILGraph graph) {
lock (this) {
if (!Contains(graph)) return false;
base.Remove(graph);
// recreate clipping regions
m_clippingData.EventingSuspend();
m_clippingData.Reset();
foreach (ILGraph gr in this) {
m_clippingData.Update (gr.Limits);
}
m_clippingData.EventingResume();
OnChange(graph,GraphCollectionChangeReason.Deleted,null);
Invalidate();
return true;
}
}
#endregion
#region public interface
public void Invalidate() {
foreach (ILGraph g in this) {
g.Invalidate();
}
}
internal void GetSortedList4Render(ILCamera camera, List sortList) {
m_unsortedCache.Clear();
foreach (ILGraph g in this) {
if (g.Is3DGraph()) {
sortList.Add(g);
} else {
m_unsortedCache.Add(g);
}
}
m_graphComparer.Camera = camera;
sortList.Sort(m_graphComparer);
sortList.AddRange(m_unsortedCache);
}
#endregion
#region private helper
#endregion
}
}