/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { object ChartObject = null; List <double> listContents = new List <double>(); List <string> names = new List <string>(); string Title = null; string SubTitle = null; //Get the Chart object and assign it if (!DA.GetData <object>("Chart to modify", ref ChartObject)) { return; } var ChartElem = HUI_Util.GetUIElement <ChartBase>(ChartObject); //set new title and subtitle or get old ones if (DA.GetData <string>("Title", ref Title)) { ChartElem.ChartTitleVisibility = System.Windows.Visibility.Visible; ChartElem.ChartTitle = Title; } if (DA.GetData <string>("SubTitle", ref SubTitle)) { ChartElem.ChartSubTitle = SubTitle; } chartModel = ChartElem.DataContext as SeriesModel; if (chartModel == null) { chartModel = new SeriesModel(); } bool valuesSupplied = DA.GetDataList <double>("New Chart Values", listContents); bool namesSupplied = DA.GetDataList <string>("New Chart Names", names); //make sure there are the same number of names and values if (valuesSupplied && namesSupplied && (names.Count != listContents.Count)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Different number of names and values supplied."); return; } //if the data isnt supplied modify it if (valuesSupplied) { //replace the ones already in there for (int i = 0; i < chartModel.Chart.Count; i++) { if (listContents.Count > i) { chartModel.Chart[i].Number = (float)listContents[i]; } } //add any on the end if (listContents.Count > chartModel.Chart.Count) { for (int i = chartModel.Chart.Count; i < listContents.Count; i++) { chartModel.Chart.Add(new ChartItem() { Number = (float)listContents[i], Category = "UNSET" }); } } //subtract any off the end if (listContents.Count < chartModel.Chart.Count) { for (int i = chartModel.Chart.Count - 1; i >= listContents.Count; i--) { chartModel.Chart.RemoveAt(i); } } } //check if the names array is different. We don't want to fire a full update unless we absolutely have to. bool hasChanged = false; var alreadyNames = chartModel.Chart.Select(item => item.Category).ToArray(); //if the length of names is different, we know it's changed if (alreadyNames.Length != names.Count()) { hasChanged = true; } else //if the length is the same, check each item { for (int i = 0; i < alreadyNames.Count(); i++) { if (alreadyNames[i] != names[i]) { hasChanged = true; break; } } } //if the data is supplied and different than what's in the chart, modify it if (namesSupplied && hasChanged) { //replace the ones already in there for (int i = 0; i < chartModel.Chart.Count; i++) { if (names.Count > i) { chartModel.Chart[i].Category = names[i]; } } //add any on the end if (names.Count > chartModel.Chart.Count) { for (int i = chartModel.Chart.Count; i < names.Count; i++) { chartModel.Chart.Add(new ChartItem() { Category = names[i], Number = float.NaN }); } } //subtract any off the end if (names.Count < chartModel.Chart.Count) { for (int i = chartModel.Chart.Count - 1; i >= names.Count; i--) { chartModel.Chart.RemoveAt(i); } } //refresh display - the binding works when the values change but not when categories do. var Values = chartModel.Chart.ToArray(); chartModel.Chart.Clear(); foreach (var item in Values) { chartModel.Chart.Add(item); } } }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { var Collection = new ObservableCollection <ChartItem>(); string Title = ""; string SubTitle = ""; List <double> listContents = new List <double>(); List <string> names = new List <string>(); int chartType = 0; //get GH input data DA.GetDataList <double>("Data", listContents); DA.GetDataList <string>("Names", names); bool hasTitle = DA.GetData <string>("Title", ref Title); bool hasSubTitle = DA.GetData <string>("SubTitle", ref SubTitle); DA.GetData <int>("Chart Type", ref chartType); ChartBase ChartElem = null; switch (chartType) { case 0: var pieElem = new PieChart(); ChartElem = pieElem; break; case 1: var barElem = new ClusteredBarChart(); ChartElem = barElem; break; case 2: var columnElem = new ClusteredColumnChart(); ChartElem = columnElem; break; case 3: var doughnutElem = new DoughnutChart(); ChartElem = doughnutElem; break; case 4: var gaugeElem = new RadialGaugeChart(); ChartElem = gaugeElem; break; default: var defaultElem = new PieChart(); ChartElem = defaultElem; break; } //Create the chart and give it a name ChartElem.ChartTitle = Title; ChartElem.ChartTitleVisibility = hasTitle ? Visibility.Visible : Visibility.Collapsed; ChartElem.ChartSubTitle = SubTitle; //package the data into a custom chart model and series SeriesModel vm = new SeriesModel(names.ToList(), listContents.ToList()); ChartElem.DataContext = vm; ChartSeries series = new ChartSeries(); series.SeriesTitle = " "; series.DisplayMember = "Category"; series.ValueMember = "Number"; //set up the data binding for the series - this is useful so it can be reset later without redrawing the whole Chart Binding seriesBinding = new Binding(); seriesBinding.Source = vm; seriesBinding.Path = new PropertyPath("Chart"); BindingOperations.SetBinding(series, ChartSeries.ItemsSourceProperty, seriesBinding); // series.ItemsSource = vm.Chart; //Pass data to the chart ChartElem.Series.Add(series); ChartElem.ToolTipFormat = "{}Caption: {0}, Value: '{1}', Series: '{2}', Percentage: {3:P2}"; ChartElem.MinWidth = 10; ChartElem.MinHeight = 10; DA.SetData("Chart", new UIElement_Goo(ChartElem, "Chart Elem", InstanceGuid, DA.Iteration)); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { object GraphObject = null; List <double> listContents = new List <double>(); List <string> names = new List <string>(); string Title = null; string SubTitle = null; //Get the Graph object and assign it if (!DA.GetData <object>("Graph to modify", ref GraphObject)) { return; } var ChartElem = HUI_Util.GetUIElement <ChartBase>(GraphObject); //set new title and subtitle or get old ones if (!DA.GetData <string>("Title", ref Title)) { Title = ChartElem.ChartTitle; } if (!DA.GetData <string>("SubTitle", ref SubTitle)) { SubTitle = ChartElem.ChartSubTitle; } //extract existing data from graph element SeriesModel dataExtractor = new SeriesModel(); ChartSeries series = ChartElem.Series[0]; dataExtractor.Chart = series.ItemsSource as ObservableCollection <ChartItem>; //if the data isnt supplied get it from old graph if (!DA.GetDataList <double>("New Pie Graph Values", listContents)) { for (int i = 0; i < dataExtractor.Chart.Count; i++) { listContents.Add(dataExtractor.Chart[i].Number); } } //if the data isnt supplied get it from old graph if (!DA.GetDataList <string>("New Pie Graph Names", names)) { for (int i = 0; i < dataExtractor.Chart.Count; i++) { names.Add(dataExtractor.Chart[i].Category); } } //make sure there are the same number of names and values if (names.Count != listContents.Count) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "different number of names and values supplied"); } //reconstruct graph data SeriesModel vm = new SeriesModel(names.ToList(), listContents.ToList()); //assign new values back to graph series.ItemsSource = vm.Chart; ChartElem.ChartTitle = Title; ChartElem.ChartSubTitle = SubTitle; //DA.SetData("TEST", new UIElement_Goo(ChartElem, "Chart Elem", InstanceGuid, DA.Iteration)); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { object ChartObject = null; GH_Structure <GH_Number> NewValueTree = new GH_Structure <GH_Number>(); List <string> names = new List <string>(); string Title = ""; string SubTitle = ""; List <string> Clustertitle = new List <string>(); //Get the Chart object and assign it if (!DA.GetData <object>("Chart to modify", ref ChartObject)) { return; } var ChartElem = HUI_Util.GetUIElement <ChartBase>(ChartObject); //set new title and subtitle or get old ones if (DA.GetData <string>("Title", ref Title)) { ChartElem.ChartTitleVisibility = System.Windows.Visibility.Visible; ChartElem.ChartTitle = Title; } if (DA.GetData <string>("SubTitle", ref SubTitle)) { ChartElem.ChartSubTitle = SubTitle; } bool ClusterTitleSupplied = DA.GetDataList <string>("ClusterTitle", Clustertitle); bool valuesSupplied = DA.GetDataTree <GH_Number>("New Chart Values", out NewValueTree); bool namesSupplied = DA.GetDataList <string>("New Chart Names", names); // MultiChartModel mcm = ChartElem.DataContext as MultiChartModel; //check if the cluster titles have changed to avoid unnecessary full redraws. bool clusterTitleChanged = false; if (ClusterTitleSupplied) { var alreadyClusterTitles = ChartElem.Series.Select(s => s.SeriesTitle).ToArray(); if (Clustertitle.Count() != alreadyClusterTitles.Length) { clusterTitleChanged = true; } else { for (int i = 0; i < alreadyClusterTitles.Length; i++) { if (alreadyClusterTitles[i] != Clustertitle[i]) { clusterTitleChanged = true; break; } } } } //check if the names array is different. We don't want to fire a full update unless we absolutely have to. bool haveNamesChanged = false; var firstModel = ChartElem.Series[0].DataContext as SeriesModel; var alreadyNames = firstModel.Chart.Select(item => item.Category).ToArray(); if (namesSupplied) { //if the length of names is different, we know it's changed if (alreadyNames.Length != names.Count()) { haveNamesChanged = true; } else //if the length is the same, check each item { for (int i = 0; i < alreadyNames.Count(); i++) { if (alreadyNames[i] != names[i]) { haveNamesChanged = true; break; } } } } else { names = alreadyNames.ToList(); } bool removedCharts = false; //Remove extra data if (NewValueTree.Branches.Count < ChartElem.Series.Count()) { removedCharts = true; for (int k = ChartElem.Series.Count() - 1; k >= NewValueTree.Branches.Count; k--) { ChartElem.Series.RemoveAt(k); } } bool addedCharts = false; for (int j = 0; j < NewValueTree.Branches.Count; j++) { List <Double> valueList = NewValueTree.Branches[j].Select(n => n.Value).ToList(); //modify existing series if (ChartElem.Series.Count > j) { var ChartSeries = ChartElem.Series[j]; var model = ChartSeries.DataContext as SeriesModel; if (ClusterTitleSupplied) { ChartSeries.SeriesTitle = Clustertitle[j]; } //make sure there are the same number of names and values if (valuesSupplied && namesSupplied && (names.Count != valueList.Count)) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Different number of names and values supplied."); return; } //if the data isnt supplied modify it if (valuesSupplied) { //replace the ones already in there for (int i = 0; i < model.Chart.Count; i++) { if (valueList.Count > i) { model.Chart[i].Number = (float)valueList[i]; } } //add any on the end if (valueList.Count > model.Chart.Count) { for (int i = model.Chart.Count; i < valueList.Count; i++) { model.Chart.Add(new ChartItem() { Number = (float)valueList[i], Category = "UNSET" }); } } //subtract any off the end if (valueList.Count < model.Chart.Count) { for (int i = model.Chart.Count - 1; i >= valueList.Count; i--) { model.Chart.RemoveAt(i); } } } //if the data is supplied and different than what's in the chart, modify it if (namesSupplied && haveNamesChanged) { //replace the ones already in there for (int i = 0; i < model.Chart.Count; i++) { if (names.Count > i) { model.Chart[i].Category = names[i]; } } //add any on the end if (names.Count > model.Chart.Count) { for (int i = model.Chart.Count; i < names.Count; i++) { model.Chart.Add(new ChartItem() { Category = names[i], Number = float.NaN }); } } //subtract any off the end if (names.Count < model.Chart.Count) { for (int i = model.Chart.Count - 1; i >= names.Count; i--) { model.Chart.RemoveAt(i); } } } if (haveNamesChanged || clusterTitleChanged) { //refresh display - the binding works when the values change but not when categories do. var Values = model.Chart.ToArray(); model.Chart.Clear(); foreach (var item in Values) { model.Chart.Add(item); } } } else { addedCharts = true; //Add new series SeriesModel vm = new SeriesModel(names.ToList(), valueList); ChartSeries series = new ChartSeries(); //We have to set the series data context rather than the whole chart series.DataContext = vm; series.SeriesTitle = Clustertitle[j]; series.DisplayMember = "Category"; series.ValueMember = "Number"; Binding seriesBinding = new Binding(); seriesBinding.Source = vm; seriesBinding.Path = new PropertyPath("Chart"); BindingOperations.SetBinding(series, ChartSeries.ItemsSourceProperty, seriesBinding); ChartElem.Series.Add(series); } } if (addedCharts || removedCharts) { var oldSeries = ChartElem.Series; ChartElem.SeriesSource = null; // ChartElem.Series.Clear(); ChartElem.SeriesSource = oldSeries; } }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { var Collection = new ObservableCollection <ChartItem>(); string Title = ""; string SubTitle = ""; List <string> Clustertitle = new List <string>(); GH_Structure <GH_Number> treeValues = new GH_Structure <GH_Number>(); //GH_Structure<GH_String> treeNames = new GH_Structure<GH_String>(); List <string> names = new List <string>(); int chartType = 0; //get GH input data bool hasTitle = DA.GetData <string>("Title", ref Title); DA.GetData <string>("SubTitle", ref SubTitle); DA.GetDataList <string>("ClusterTitle", Clustertitle); DA.GetDataTree <GH_Number>("Data", out treeValues); //DA.GetDataTree<GH_String>("Names", out treeNames); DA.GetDataList <string>("Names", names); DA.GetData <int>("Chart Type", ref chartType); ChartBase ChartElem = null; switch (chartType) { case 0: var ColumnCluster = new ClusteredColumnChart(); ChartElem = ColumnCluster; break; case 1: var BarCluster = new ClusteredBarChart(); ChartElem = BarCluster; break; case 2: var ColumnStack = new StackedColumnChart(); ChartElem = ColumnStack; break; case 3: var BarStack = new StackedBarChart(); ChartElem = BarStack; break; case 4: var pieElem = new PieChart(); ChartElem = pieElem; break; default: var defaultElem = new ClusteredBarChart(); ChartElem = defaultElem; break; } //Give the chart its name ChartElem.ChartTitle = Title; ChartElem.ChartSubTitle = SubTitle; // MultiChartModel mcm = new MultiChartModel(); // mcm.Series = new ObservableCollection<SeriesModel>(); for (int i = 0; i < treeValues.Branches.Count; i++) { //package the data into a custom chart model and series List <double> listDouble = treeValues[i].ConvertAll(x => x.Value); SeriesModel vm = new SeriesModel(names.ToList(), listDouble, Clustertitle[i]); ChartSeries series = new ChartSeries(); //We have to set the series data context rather than the whole chart series.DataContext = vm; series.SeriesTitle = Clustertitle[i]; series.DisplayMember = "Category"; series.ValueMember = "Number"; Binding seriesBinding = new Binding(); seriesBinding.Source = vm; seriesBinding.Path = new PropertyPath("Chart"); BindingOperations.SetBinding(series, ChartSeries.ItemsSourceProperty, seriesBinding); ChartElem.Series.Add(series); //Pass data to the chart // mcm.Series.Add(vm); } //Binding seriesSetBinding = new Binding(); //seriesSetBinding.Source = mcm; //seriesSetBinding.Path = new PropertyPath("Series"); //BindingOperations.SetBinding(ChartElem, ChartBase.SeriesSourceProperty, seriesSetBinding); ////Send Data to GH output DA.SetData("MultiChart", new UIElement_Goo(ChartElem, "Chart Elem", InstanceGuid, DA.Iteration)); // DA.SetData("Test","listNames"); }