## "Parallel Coordinates" Charts

Need help in implementing some specific function to your LightningChart Ultimate powered application? Post a question and get code snippets from other LightningChart Ultimate community members.

Moderator: Arction_LasseP

deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

### "Parallel Coordinates" Charts

To visualize high-dimensional multivariate data, a "Parallel Coordinates" chart can be used. See Wikipedia's page for Parallel Coordinates here: http://en/wikipedia.org/wiki/Parallel_coordinates

I am wondering if anyone has suggestions for an easy way to implement such a chart using LightningChart, and/or if you already have an implementation that you wouldn't mind sharing, or at least the concepts of how you might go about implementing it with LightningChart.

I am interested in 2D multi-Y axis implementations and 3D multi-Z plane implementations.

ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

### Re: "Parallel Coordinates" Charts

Hi Deanius,

That can be done with LC. We'll first make a parallel coordinates 2D example by beginning of next week.
LightningChart Support Team, PT

ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

### Re: "Parallel Coordinates" Charts

Here's an example of 2D parallel coordinates chart, made with LightningChart component. it's for WinForms. If you are using WPF, the code is almost identical.

Code: Select all

``````// Chart.
private LightningChartUltimate m_chart;

//Attribute count
private const int AttributeCount = 6;

//List of persons
private List<BasicPersonInfo> m_listPersons = new List<BasicPersonInfo>();

/// <summary>
/// Constructor.
/// </summary>
public ExampleParallelCoordinates()
{
m_chart = null;

InitializeComponent();

//Create person attributes data, some info gathered when visiting a nurse
m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68));

CreateChart();
}

public enum EyeColor
{
Blue = 1,
Brown,
Green,
Gray,
}

public class BasicPersonInfo
{
public double Weight_kg;
public double Height_cm;
public EyeColor ColorOfEyes;
public int Age;
public double BloodPressureSystolic_mmHg;
public double BloodPressureDiastolic_mmHg;

public BasicPersonInfo(double weight, double height, EyeColor colorOfEyes, int age, double bpSys, double bpDia)
{
this.Weight_kg = weight;
this.Height_cm = height;
this.ColorOfEyes = colorOfEyes;
this.Age = age;
this.BloodPressureSystolic_mmHg = bpSys;
this.BloodPressureDiastolic_mmHg = bpDia;
}

public double[] GetValues()
{
return new double[] {this.Weight_kg, this.Height_cm, (double)this.ColorOfEyes, this.Age, this.BloodPressureSystolic_mmHg, this.BloodPressureDiastolic_mmHg};
}
}

/// <summary>
/// Create chart.
/// </summary>
private void CreateChart()
{
//Create new chart

//Disable rendering, strongly recommended before updating chart properties
m_chart.BeginUpdate();

//Chart parent must be set
m_chart.Parent = this;

//Fill parent area with chart
m_chart.Dock = DockStyle.Fill;

//Chart name
m_chart.Name = "Parallel coordinates chart";

//Hide legend box
m_chart.ViewXY.LegendBox.Visible = true;

//Create an Y axis for each attribute
m_chart.ViewXY.YAxes.Clear();
double dPositionInterval = 100.0 / ((double) AttributeCount - 1.0); //interval of categories in range 0...100

//Weight axis. This is the 'master' axis
AxisY axisYWeight = new AxisY(m_chart.ViewXY);
axisYWeight.SetRange(40, 100);
axisYWeight.Title.Text = "Weight / kg";
axisYWeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Height axis, 'slave' axis
AxisY axisYHeight = new AxisY(m_chart.ViewXY);
axisYHeight.SetRange(140, 200);
axisYHeight.Title.Text = "Height / cm";
axisYHeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Eye color axis 'slave' axis
AxisY axisYEyeColor = new AxisY(m_chart.ViewXY);
axisYEyeColor.SetRange(1, 4);
axisYEyeColor.Title.Text = "Eye color";
{
new CustomAxisTick (axisYEyeColor, 1, "Blue"),
new CustomAxisTick(axisYEyeColor,2, "Brown"),
new CustomAxisTick(axisYEyeColor,3, "Green"),
new CustomAxisTick(axisYEyeColor,4, "Gray"),
});
axisYEyeColor.CustomTicksEnabled = true;
axisYEyeColor.AutoFormatLabels = false;
axisYEyeColor.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Age axis 'slave' axis
AxisY axisYAge = new AxisY(m_chart.ViewXY);
axisYAge.SetRange(10, 90);
axisYAge.Title.Text = "Age / years";
axisYAge.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Blood pressure, systolic, 'slave' axis
AxisY axisYBpSys = new AxisY(m_chart.ViewXY);
axisYBpSys.SetRange(60, 180);
axisYBpSys.Title.Text = "Blood pressure (sys)/ mmHg ";
axisYBpSys.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Blood pressure, diastolic, 'slave' axis
AxisY axisYBpDia = new AxisY(m_chart.ViewXY);
axisYBpDia.SetRange(60, 180);
axisYBpDia.Title.Text = "Blood pressure (dia)/ mmHg ";
axisYBpDia.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

foreach(AxisY axisY in m_chart.ViewXY.YAxes)
{
axisY.MajorGrid.Visible = false;
axisY.AxisThickness = 1;
axisY.RangeChanged += new AxisBase.RangeChangedHandler(axisY_RangeChanged);
}

//Setup x-axis
m_chart.ViewXY.XAxes[0].Visible = false;
m_chart.ViewXY.XAxes[0].SetRange(0, 5);

//Disable X zooming and panning
m_chart.ViewXY.ZoomPanOptions.RectangleZoomDirectionLayered = RectangleZoomDirectionLayered.Vertical;
m_chart.ViewXY.ZoomPanOptions.PanDirection = PanDirection.Vertical;
m_chart.ViewXY.ZoomPanOptions.MouseWheelZooming = MouseWheelZooming.Vertical;

//Turn of automatic Y axis placement so we can place them manually with Position property
m_chart.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.Off;

int iPerson = 0;

//Add series for each person and bind them to the first Y axis
foreach (BasicPersonInfo person in m_listPersons)
{
PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
pls.PointsVisible = true;
pls.Title.Text = "Person" + (iPerson + 1).ToString();

SeriesPoint[] aPoints = new SeriesPoint[AttributeCount];
double[] attributeValues = person.GetValues();
for(int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
{
aPoints[iAttrib].X = iAttrib;
aPoints[iAttrib].Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]);
}

pls.LineStyle.Color = DefaultColors.SeriesForBlackBackground[iPerson];
pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackground[iPerson];
pls.Points = aPoints;

//Add the created point line series into PointLineSeries list

iPerson++;
}

//Allow chart rendering
m_chart.EndUpdate();
}

/// <summary>
/// Scale given attribute to master axis scale, by converting the attribute value to screen coordinate and then to master axis range
/// </summary>
/// <param name="attributeIndex">Attribute index</param>
/// <param name="value">Attribute value</param>
/// <returns>Scaled value</returns>
double ScaleToMasterAxis(int attributeIndex, double value)
{
AxisY masterAxis = m_chart.ViewXY.YAxes[0];
AxisY attributeAxis = m_chart.ViewXY.YAxes[attributeIndex];
float fYCoord = attributeAxis.ValueToCoord(value);
double dYValueOnMasterAxis = 0;
masterAxis.CoordToValue(fYCoord, out dYValueOnMasterAxis);
return dYValueOnMasterAxis;

}

void axisY_RangeChanged(double newMin, double newMax, AxisBase axis, ref bool cancelRendering)
{
m_chart.BeginUpdate();

int iPerson = 0;
foreach (BasicPersonInfo person in m_listPersons)
{
double[] attribValues = person.GetValues();
PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];

//Update all other attributes, to master axis scale
for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
{
series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
}

series.InvalidateData();
iPerson++;
}

m_chart.EndUpdate();
}
``````
Parallel coordinates chart
ParallelCoordinatesChart.jpg (253.88 KiB) Viewed 24120 times
The axes by categories are draggable and the view is zoomable vertically.

Does this help?
LightningChart Support Team, PT

deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

### Re: "Parallel Coordinates" Charts

Yes, this helps out a lot. And, yes, we are using WPF.

I'm assuming that a 3D implementation wouldn't be too much different. If it's not too much trouble, can you put together a 3D sample as well? No hurry...

I'm really impressed by your quick response for a sample. Thank you very much.

ArctionPasi
Posts: 1366
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

### Re: "Parallel Coordinates" Charts

Hi Deanius,

I made this kind of prototype...
3D Parallel Coordinates Chart
ParallelCoordinatesChart3D.jpg (400.46 KiB) Viewed 24071 times
This is for WPF

Code: Select all

``````using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Arction.WPF.LightningChartUltimate;
using Arction.WPF.LightningChartUltimate.Series3D;
using Arction.WPF.LightningChartUltimate.Views.View3D;
using Arction.WPF.LightningChartUltimate.Axes;

namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
//Chart
private LightningChartUltimate m_chart = null;

public MainWindow()
{
InitializeComponent();
CreateChart();
}

/// <summary>
/// Create chart.
/// </summary>
private void CreateChart()
{
//Create new chart
m_chart = new LightningChartUltimate();

//Disable rendering, strongly recommended before updating chart properties
m_chart.BeginUpdate();

//Set active view
m_chart.ActiveView = ActiveView.View3D;

//Chart parent must be set

//Set camera, 3D world dimensions etc.
m_chart.View3D.Camera.RotationY -= 40;
m_chart.View3D.Dimensions.Z = 150;
m_chart.View3D.XAxisPrimary3D.Reversed = true;
m_chart.View3D.ZAxisPrimary3D.Title.Visible = false;
m_chart.View3D.LegendBox.ShowCheckboxes = false;

//Set data
SetData();

//Allow chart rendering
m_chart.EndUpdate();

}
private void SetData()
{

//Disable rendering, strongly recommended before updating chart properties
m_chart.BeginUpdate();

View3D v = m_chart.View3D;

//Clear existing series and their data
v.PointLineSeries3D.Clear();

int iAttribCount = 5;
int iGroupCount = 10;
int iLinesPerGroup = 50;

double dVariationX = 40;
double dVariationY = 30;
double xMin = v.XAxisPrimary3D.Minimum;
double xMax = v.XAxisPrimary3D.Maximum;
double yMin = v.YAxisPrimary3D.Minimum;
double yMax = v.YAxisPrimary3D.Maximum;
double dXShiftPerGroup = (xMax - xMin) / (double)(iGroupCount + 1);

Random rand = new Random();

for (int iGroup = 0; iGroup < iGroupCount; iGroup++)
{
Color colorGroupLine = DefaultColors.SeriesForBlackBackgroundWPF[iGroup];

for (int iLine = 0; iLine < iLinesPerGroup; iLine++)
{
PointLineSeries3D series = new PointLineSeries3D(v, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);
series.LineStyle.Color = colorGroupLine;
series.LineStyle.Width = 2;
series.LineStyle.AntiAliasing = LineAntialias.None;
series.PointsVisible = false;
series.Title.ShowInLegendBox = iLine == 0; //Only show the first line in the legend box of each group
series.Title.Text = "Data group " + (iGroup + 1).ToString();

PointDouble3D[] points = new PointDouble3D[iAttribCount];
for (int i = 0; i < iAttribCount; i++)
{
points[i].X = (double)(iGroup + 1) * dXShiftPerGroup + dVariationX / 2.0 * (rand.NextDouble() - 0.5) + (rand.NextDouble() - 0.5) * Math.Sin((double)iLine);
points[i].Y = 30 + dVariationY * rand.NextDouble() + dVariationY * Math.Sin(iGroup / 3.0) * Math.Sin((double)i * 5.0);// (Math.Sin((double)i/3.0) * (double)(iGroup) / 3.0 *Math.Sin(iGroup)/(double)iGroupCount);
points[i].Z = i;
}

series.Points = points;
}
}

//Set Z axis range according to attribute count
Axis3DBase zAxis = v.ZAxisPrimary3D;
zAxis.SetRange(-0.5, iAttribCount - 0.5);

//Set custom ticks to explain the attributes and planes
for (int i = 0; i < iAttribCount; i++)
{
zAxis.CustomTicks.Add(new CustomAxisTick(zAxis, i, "Attribute " + (i + 1).ToString()));
}
zAxis.CustomTicksEnabled = true;
zAxis.AutoFormatLabels = false;

//Create XY plane for each attribute
v.Rectangles.Clear();

for (int i = 0; i < iAttribCount; i++)
{
Rectangle3D rect = new Rectangle3D(v, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);

rect.Fill.Color = Color.FromArgb(120, 64, 64, 64);

//These are 3D world coordinates
rect.Center.X = (xMax + xMin) / 2.0;
rect.Center.Y = (yMax + yMin) / 2.0;
rect.Center.Z = i;

rect.Size.Width = v.Dimensions.X;
rect.Size.Height = v.Dimensions.Y;

rect.Rotation.X = 90;

}

//Allow chart rendering
m_chart.EndUpdate();
}
}
}

``````
I hope this helps...
LightningChart Support Team, PT

deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

### Re: "Parallel Coordinates" Charts

Yes, this is exactly what I was looking for to get an idea of what it would take to implement Parallel coordinates in 3D space.
Thanks again.

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

namespace ParallelCoordinates
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///

public partial class MainWindow : Window
{

//List of persons
private List<BasicPersonInfo> m_listPersons = new List<BasicPersonInfo>();
private int AttributeCount = 6;

public MainWindow()
{

InitializeComponent();

//Create person attributes data, some info gathered when visiting a nurse
m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68));

CreateChart();
}

private void CreateChart()
{

m_chart.BeginUpdate();

//Chart parent must be set
// m_chart.Parent = this;

//Fill parent area with chart

//Chart name
// m_chart.Name = "Parallel coordinates chart";

//Hide legend box
m_chart.ViewXY.AutoSpaceLegendBoxes = true;

//Create an Y axis for each attribute
m_chart.ViewXY.YAxes.Clear();
double dPositionInterval = 100.0 / ((double)AttributeCount - 1.0); //interval of categories in range 0...100

//Weight axis. This is the 'master' axis
AxisY axisYWeight = new AxisY(m_chart.ViewXY);
axisYWeight.SetRange(40, 100);
axisYWeight.Title.Text = "Weight / kg";
axisYWeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Height axis, 'slave' axis
AxisY axisYHeight = new AxisY(m_chart.ViewXY);
axisYHeight.SetRange(140, 200);
axisYHeight.Title.Text = "Height / cm";
axisYHeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Eye color axis 'slave' axis
AxisY axisYEyeColor = new AxisY(m_chart.ViewXY);
axisYEyeColor.SetRange(1, 4);
axisYEyeColor.Title.Text = "Eye color";
{
new CustomAxisTick (axisYEyeColor, 1, "Blue"),
new CustomAxisTick(axisYEyeColor,2, "Brown"),
new CustomAxisTick(axisYEyeColor,3, "Green"),
new CustomAxisTick(axisYEyeColor,4, "Gray"),
});
axisYEyeColor.CustomTicksEnabled = true;
axisYEyeColor.AutoFormatLabels = false;
axisYEyeColor.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Age axis 'slave' axis
AxisY axisYAge = new AxisY(m_chart.ViewXY);
axisYAge.SetRange(10, 90);
axisYAge.Title.Text = "Age / years";
axisYAge.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Blood pressure, systolic, 'slave' axis
AxisY axisYBpSys = new AxisY(m_chart.ViewXY);
axisYBpSys.SetRange(60, 180);
axisYBpSys.Title.Text = "Blood pressure (sys)/ mmHg ";
axisYBpSys.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

//Blood pressure, diastolic, 'slave' axis
AxisY axisYBpDia = new AxisY(m_chart.ViewXY);
axisYBpDia.SetRange(60, 180);
axisYBpDia.Title.Text = "Blood pressure (dia)/ mmHg ";
axisYBpDia.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;

foreach (AxisY axisY in m_chart.ViewXY.YAxes)
{
axisY.MajorGrid.Visible = false;
axisY.AxisThickness = 1;
axisY.RangeChanged += AxisY_RangeChanged;
//axisY.RangeChanged += new AxisBase.RangeChangedHandler(axisY_RangeChanged);
}

//Setup x-axis
m_chart.ViewXY.XAxes[0].Visible = false;
m_chart.ViewXY.XAxes[0].SetRange(0, 5);

//Disable X zooming and panning
m_chart.ViewXY.ZoomPanOptions.RectangleZoomDirection = RectangleZoomDirection.Vertical;
m_chart.ViewXY.ZoomPanOptions.PanDirection = PanDirection.Vertical;
m_chart.ViewXY.ZoomPanOptions.MouseWheelZooming = MouseWheelZooming.Vertical;

//Turn of automatic Y axis placement so we can place them manually with Position property
m_chart.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.Off;

int iPerson = 0;

//Add series for each person and bind them to the first Y axis
foreach (BasicPersonInfo person in m_listPersons)
{
PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
pls.PointsVisible = true;
pls.Title.Text = "Person" + (iPerson + 1).ToString();

SeriesPointCollection aPoints = new SeriesPointCollection();
double[] attributeValues = person.GetValues();

for (int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
{
SeriesPoint seriesPoint = new SeriesPoint();
seriesPoint.X = iAttrib;
seriesPoint.Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]);
}

pls.LineStyle.Color = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.Points = aPoints;

//Add the created point line series into PointLineSeries list

iPerson++;
}

//Allow chart rendering
m_chart.EndUpdate();
}

private void AxisY_RangeChanged(object sender, RangeChangedEventArgs e)
{
m_chart.BeginUpdate();

int iPerson = 0;
foreach (BasicPersonInfo person in m_listPersons)
{
double[] attribValues = person.GetValues();
PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];

//Update all other attributes, to master axis scale
for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
{
series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
}

series.InvalidateData();
iPerson++;
}
}

public class BasicPersonInfo
{
public double Weight_kg;
public double Height_cm;
public EyeColor ColorOfEyes;
public int Age;
public double BloodPressureSystolic_mmHg;
public double BloodPressureDiastolic_mmHg;

public BasicPersonInfo(double weight, double height, EyeColor colorOfEyes, int age, double bpSys, double bpDia)
{
this.Weight_kg = weight;
this.Height_cm = height;
this.ColorOfEyes = colorOfEyes;
this.Age = age;
this.BloodPressureSystolic_mmHg = bpSys;
this.BloodPressureDiastolic_mmHg = bpDia;
}

public double[] GetValues()
{
return new double[] { this.Weight_kg, this.Height_cm, (double)this.ColorOfEyes, this.Age, this.BloodPressureSystolic_mmHg, this.BloodPressureDiastolic_mmHg };
}
}
public enum EyeColor
{
Blue = 1,
Brown,
Green,
Gray,
}
double ScaleToMasterAxis(int attributeIndex, double value)
{
AxisY masterAxis = m_chart.ViewXY.YAxes[0];
AxisY attributeAxis = m_chart.ViewXY.YAxes[attributeIndex];
float fYCoord = attributeAxis.ValueToCoord(value);
double dYValueOnMasterAxis = 0;
masterAxis.CoordToValue(fYCoord, out dYValueOnMasterAxis);
return dYValueOnMasterAxis;
}

}
}

This is my code I am trying to make Parallel coordinates but my Scaletomatrix is always returning 100 due to which lines are not creating please help me

Arction_LasseP
Posts: 105
Joined: Wed Mar 27, 2019 1:05 pm

### Re: "Parallel Coordinates" Charts

Hello,

We checked the code you posted and managed to get it working. The main issue that stopped it from working was that there is m_chart.BeginUpdate() inside the AxisY_RangeChanged -event but no EndUpdate(). This means that every time axis range is changed, the chart disables rendering, and since it never properly enables it, the PointLineSeries defined inside the event most likely won't get rendered or updated at all. InvalidateData() called inside the event notifies the chart that there is something new to render but doesn't help here as the rendering is disabled. The solution for this is to either add m_chart.EndUpdate() or remove the BeginUpdate().

Another small note is the lack of AfterRendering -event that was used in our original parallel coordinates example. Without it the series might not be rendered unless there is some interaction with the axes.

Hope this helps.
Best regards,
Lasse

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

private void AxisY_RangeChanged(object sender, RangeChangedEventArgs e)
{
m_chart.BeginUpdate();

int iPerson = 0;
foreach (BasicPersonInfo person in m_listPersons)
{
double[] attribValues = person.GetValues();
PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];

//Update all other attributes, to master axis scale
for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
{
series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
}

series.InvalidateData();
iPerson++;

}
m_chart.EndUpdate();
}

I have tried this and the other way around by removing m_chart.BeginUpdate(); the lines are not generating.....

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

I also don't understand where are you using afterrendering?

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

This is the outcome i am getting.
Attachments
This is outcome i am getting
pic.PNG (117.42 KiB) Viewed 8998 times

Arction_LasseP
Posts: 105
Joined: Wed Mar 27, 2019 1:05 pm

### Re: "Parallel Coordinates" Charts

Hello,

If you check our ParallelCoordinates -demo source code, you can see that it adds the PointLineSeries to the chart inside AfterRendering -event, not in CreateChart() -method as in your example code. Usually it is better to add the series when creating the chart, but this particular example is a special case; it uses screen coordinates to define the positions of the PointLineSeries. To use screen coordinates, the chart has to be rendered first.

Code: Select all

``````// In CreateChart() subscribe to the event
m_chart.AfterRendering += m_chart_AfterRendering;

private void m_chart_AfterRendering(object sender, AfterRenderingEventArgs e)
{
// We no longer need to handle this event so unsubscibe from it.
m_chart.AfterRendering -= m_chart_AfterRendering;

m_chart.BeginUpdate();

int iPerson = 0;

//Add series for each person and bind them to the first Y axis
foreach (BasicPersonInfo person in m_listPersons)
{
PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
pls.PointsVisible = true;
pls.Title.Text = "Person" + (iPerson + 1).ToString();

//SeriesPointCollection aPoints = new SeriesPointCollection();
SeriesPoint[] aPoints = new SeriesPoint[AttributeCount];
double[] attributeValues = person.GetValues();

for (int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
{
SeriesPoint seriesPoint = new SeriesPoint();
seriesPoint.X = iAttrib;
seriesPoint.Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]);
aPoints[iAttrib] = seriesPoint;
}

pls.LineStyle.Color = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.Points = aPoints;

//Add the created point line series into PointLineSeries list

iPerson++;
}
m_chart.EndUpdate();
}
``````
Note that we unsubscribe from the event immediately after it's been triggered, as it is no longer needed.

Another possible reason for this issue is that in your code a collection is used instead of an array:
SeriesPointCollection aPoints = new SeriesPointCollection();

Unless you are using WPF Fully-bindable platform instead of for example WPF Non-bindable, it is better to use arrays to assign data points as shown also in the code above.

Best regards,
Lasse

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

Thank you so much for your help that works for me. Just one more thing in your example above you are showing titles with person 1 , person 2 and so on how are you doing it as i am following exactly the same code but it does not come up in my code.

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

### Re: "Parallel Coordinates" Charts

Plus if I wanted to add some lines with same color how can i do that where are you handling the color because when I add the one more line with same eye color it always create a new line with different color.

m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
m_listPersons.Add(new BasicPersonInfo(60, 135, EyeColor.Brown, 15, 105, 50));
m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68));

ArctionKestutis
Posts: 401
Joined: Mon Mar 14, 2016 9:22 am

### Re: "Parallel Coordinates" Charts

Hi,

This start to look like we are building proof-of-concept application. This would be part of technical support which requires active LightningChart subscription. Therefore, I would like to ask you to send subscription ID directly to Arction Support (support [at] arction com). If you don't have one please introduce yourself (company you represent, how Chart is used etc.).

All the best.