I am trying to make a simple mouse follower. A blue circle who's position updates with my mouse position. What is wrong with my update and view that won't allow this?
type alias Model = { position : { x : Int, y : Int} }
update : Msg -> Model -> (Model,Cmd.Cmd Msg)
update (MouseMsg pos) model = ({ position = {x = model.position.x + pos.x, y = model.position.y + pos.y} },Cmd.none)
view : Model -> Html.Html Msg
view model = let
posX = toString model.position.x
posY = toString model.position.y
in
svg [width "600",height "600"] [circle [cx "300",cy "300", r "50", fill "blue", x posX, y posY] []]
The <circle> SVG element does not support x or y attributes (they're silently ignored). cx and cy are the coordinates of the center, so you need to pass posX and posY to them:
Change:
circle [cx "300",cy "300", r "50", fill "blue", x posX, y posY]
To:
circle [cx posX, cy posY, r "50", fill "blue"]
Related
I want to display a babylon image but I can't position it, because a BABYLON.GUI.Image doens't seem to have properties like x, y or position.
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
const image = new BABYLON.GUI.Image("but", "bvtech_logo.jpg");
image.width = "300px";
image.height = "100px";
//The following 2 lines don't work
image.x = 10;
image.y = "10px";
advancedTexture.addControl(image);
You must use the properties left and top. The problem is that you have to remenber that if you say image.left = 0 it means that the center of the image is at the center of the canvas. So if you want the top left corner of the screen to be the origin (0, 0) you must use a utility function
const positionImage = (image, x, y) => {
const canvas = document.getElementById('myCanvas');
image.left = - canvas.width / 2 + image.width / 2 + x;
image.top = - canvas.height / 2 + image.height / 2 + y;
};
positionImage(myImage, 10, 10);
I’m a bit new to working with 3D space and rotation. I have a list of four Vector3 points that represent the corners of a rectangle. What I want to do is use those points to create a box mesh that is rotated to exactly match the angle of rotation of the points.
Here is a babylonjs playground demo showing what I mean. In it you can see I’ve drawn a simple line mesh between the points. That is great and the rectangle drawn is at the expected angle given the data. I’ve also created a box mesh and configured its dimensions to match and placed its center point in the proper center of the points. So far so good, however I cannot figure out how to rotate the box so that it’s top face is parallel with the face of the rectangle.
https://playground.babylonjs.com/#SN5K8L#2
var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
var target = new BABYLON.Vector3(1.5, 4, 0)
var camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2 + Math.PI, Math.PI / 4, 10, target, scene)
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
const axes = new BABYLON.AxesViewer(scene)
const points = [
new BABYLON.Vector3(1, 5, 1),
new BABYLON.Vector3(2, 5, 1),
new BABYLON.Vector3(2, 3, -1),
new BABYLON.Vector3(1, 3, -1)
]
const lines = BABYLON.MeshBuilder.CreateLines("lines", {
points: [...points, points[0]] // add a duplicate of first point to close polygon
}, scene)
const centerPoint = new BABYLON.Vector3(
(points[0].x + points[1].x + points[2].x + points[3].x) / 4,
(points[0].y + points[1].y + points[2].y + points[3].y) / 4,
(points[0].z + points[1].z + points[2].z + points[3].z) / 4
)
const width = Math.sqrt(
Math.pow(points[1].x - points[0].x, 2) +
Math.pow(points[1].y - points[0].y, 2) +
Math.pow(points[1].z - points[0].z, 2)
)
const depth = Math.sqrt(
Math.pow(points[2].x - points[1].x, 2) +
Math.pow(points[2].y - points[1].y, 2) +
Math.pow(points[2].z - points[1].z, 2)
)
const height = 0.15
const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
box.position = centerPoint
//box.rotation = ???
return scene;
};
You can use Vector3.RotationFromAxis to compute the required rotation in Euler angles:
const rotationAxisX = points[1].subtract(points[0])
const rotationAxisZ = points[1].subtract(points[2])
const rotationAxisY = rotationAxisZ.cross(rotationAxisX)
// RotationFromAxis has the side effect of normalising the input vectors
// so retrieve the box dimensions here
const width = rotationAxisX.length()
const depth = rotationAxisZ.length()
const height = 0.15
const rotationEuler = BABYLON.Vector3.RotationFromAxis(
rotationAxisX,
rotationAxisY,
rotationAxisZ
)
const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
box.position = centerPoint
box.rotation = rotationEuler
I'm trying to make a simple scatterpolar chart in R Plotly that has 60+ categorical labels on the radial axis. Because there's so many labels, the default method of placing the labels results in them overlapping each other at the top and bottom of the chart. I've tried setting layout.polar.angularaxis.tickangle to 90, -90, etc, but that rotates ALL labels by that amount. Is there a way to either A) rotate each individual label by a specified value such that I can set some labels to -90 degrees, others to -45, others to 0, others to +45, etc... or B) leave the label rotations at 0 (as they are now), but dodge them a bit so they don't overlap at the top and bottom of the chart? I think B would be easier and preferred, but not sure if either A or B are possible.
Here's a minimum reproducible example:
labs1 <- c("Red ", "Green ", "Blue ", "Yellow ", "Orange ",
"Purple ", "Pink ", "Black ", "White ", "Gray ")
labs2 <- c("Bookshelf", "Television", "Refridgerator",
"Toolbox", "Xylophone" , "Chromosome")
labs <- sample(paste0(rep(labs1, each = 6), rep(labs2, times = 10)), 60, replace = F)
df <- data.frame(Label = labs,
Radius = sample(0:100, 60, replace = F))
plot_ly(df) %>%
add_trace(type = 'scatterpolar', mode = 'markers',
r = ~Radius,
theta = ~Label,
fill = 'toself')
And here's the result:
It's been a while since you asked, but I just ran across your question. I've got a workaround.
If you'd like to duplicate the data I've used, I used set.seed(35) before calling the creation of df.
This uses the library htmlwidgets in addition to plotly.
I've added margin to the layout to account for label rotation. This may need to be modified. (For example, if you had really long labels, the margin would need to be larger.)
In the onRender call, I have several things happening.
The function detector: checks for labels that have collided with each other
The function angler: extracts the tick angle the label is associated with
The function quad: identifies which quadrant of the circle it's working with, then rotates the text accordingly
lbls is a node list of every label in the plot.
The for loop rotates through lbls, sending every two labels to the function detector. If a collision is detected, the indicators are sent to the angler function (which calls the quad function).
It's not perfect, but it works.
library(plotly)
library(htmlwidgets)
plot_ly(df, type = 'scatterpolar', mode = 'markers',
r = ~Radius, theta = ~Label, fill = 'toself') %>%
layout(margin = list(t = 75, r = 75, b = 75, l = 75)) %>% # make room for rotated labels
onRender(
"function(el, x) {
function detector(r1, r2) {
var r1 = r1.getBoundingClientRect(); /* catch associated real estate */
var r2 = r2.getBoundingClientRect();
return !(r2.left > r1.right || /* check for collisions */
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
function angler(ind) { /* get tick angle to rotate label */
var par = lbls[ind].parentNode.parentNode.children[ind];
var parAt = par.getAttribute('transform');
/* send extracted angle to quad function*/
quad(/(?<=rotate\\()[^)]*/.exec(parAt)[0], ind); /* using regex, after 'rotate(' -- except ')' */
}
function quad(ang, ind) {
var ang = Math.abs(Number(ang)); /* make the angle a positive number */
var gimmeXform = lbls[ind].getAttribute('transform'); /* extract values to modify */
var xer = lbls[ind].getAttribute('x'); /* 3rd & 4th quad, ctrl rotation pt */
var yer = lbls[ind].getAttribute('y'); /* 3rd & 4th quad, ctrl rotation pt */
if(ang <= 90) {
tang = ang * -.3; /* rotate ang * -x, text-anchor: start */
tanch = 'start';
} else if (ang > 90 && ang <= 180) {
tang = (180 - ang) * 0.3; /* rotate ang * +x, text-anchor: end */
tanch = 'end';
} else if (ang > 180 && ang < 270) {
tang = (ang - 180) * -.3; /* rotate ang * -x, text-anchor: end */
tang = '' + tang + ',' + xer + ',' + yer; /* add rotation controls*/
tanch = 'end';
} else {
tang = (360 - ang) * 0.3; /* rotate ang * +x, text-anchor: start */
tang = '' + tang + ',' + xer + ',' + yer; /* add rotation controls*/
tanch = 'start';
} /* rotate text */
lbls[ind].setAttribute('transform', gimmeXform + 'rotate(' + tang + ')');
lbls[ind].setAttribute('text-anchor', tanch);
}
lbls = document.querySelectorAll('g.angularaxistick > text');
inds = [];
for(i = 0; i < lbls.length; i+=2) {
if(detector(lbls[i], lbls[i + 1])) { /* if collision detected */
angler(i);
angler(i + 1);
}
}
}"
)
Whether you've plotted it with a ton of real estate:
Or very little real estate, it alters the labels:
FYI: if all of the labels overlap, it won't work.
I would like to create a 3D demo application with JavaFX to visualize movements of points in 3D space and first I need to set up a coordinate grid for visual reference. Unfortunately, I was not able to find a sample code for a grid like in this picture:
Does anyone know what is the most practical way to create something like it?
There are a few solutions out there already.
FXyz3D library has a CubeWorld class, that gives you precisely a reference grid.
It is quite easy to use. Just import the 'org.fxyz3d:fxyz3d:0.3.0' dependency from JCenter and use it:
CubeWorld cubeWorld = new CubeWorld(5000, 500, true);
Sphere sphere = new Sphere(100);
sphere.setMaterial(new PhongMaterial(Color.FIREBRICK));
sphere.getTransforms().add(new Translate(100, 200, 300));
Scene scene = new Scene(new Group(cubeWorld, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
As you can see, the solution is based on using 2D rectangles for each face, and the grid lines are created with 3D cylinders. It has very nice features (like self lightning or frontal faces according to camera don't show grid), but it is quite intensive in nodes (sample above has 168 nodes).
There are other solutions that use a lower number of nodes. For instance, for this sample, that also happens to be related to Leap Motion, I used a TriangleMesh.
This is an easy solution, and with just two meshes. However, you see the triangles, instead of squares.
So let's try to get rid of the triangles. For that I'll use a PolygonMesh, as in this other question, based on the 3DViewer project that is available at the OpenJFX repository, contains already a PolygonalMesh implementation, that allows any number of points per face, so any polygon can be a face.
This will give you a plane grid based in square faces:
private PolygonMesh createQuadrilateralMesh(float width, float height, int subDivX, int subDivY) {
final float minX = - width / 2f;
final float minY = - height / 2f;
final float maxX = width / 2f;
final float maxY = height / 2f;
final int pointSize = 3;
final int texCoordSize = 2;
// 4 point indices and 4 texCoord indices per face
final int faceSize = 8;
int numDivX = subDivX + 1;
int numVerts = (subDivY + 1) * numDivX;
float points[] = new float[numVerts * pointSize];
float texCoords[] = new float[numVerts * texCoordSize];
int faceCount = subDivX * subDivY;
int faces[][] = new int[faceCount][faceSize];
// Create points and texCoords
for (int y = 0; y <= subDivY; y++) {
float dy = (float) y / subDivY;
double fy = (1 - dy) * minY + dy * maxY;
for (int x = 0; x <= subDivX; x++) {
float dx = (float) x / subDivX;
double fx = (1 - dx) * minX + dx * maxX;
int index = y * numDivX * pointSize + (x * pointSize);
points[index] = (float) fx;
points[index + 1] = (float) fy;
points[index + 2] = 0.0f;
index = y * numDivX * texCoordSize + (x * texCoordSize);
texCoords[index] = dx;
texCoords[index + 1] = dy;
}
}
// Create faces
int index = 0;
for (int y = 0; y < subDivY; y++) {
for (int x = 0; x < subDivX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
int tc00 = y * numDivX + x;
int tc01 = tc00 + 1;
int tc10 = tc00 + numDivX;
int tc11 = tc10 + 1;
faces[index][0] = p00;
faces[index][1] = tc00;
faces[index][2] = p10;
faces[index][3] = tc10;
faces[index][4] = p11;
faces[index][5] = tc11;
faces[index][6] = p01;
faces[index++][7] = tc01;
}
}
int[] smooth = new int[faceCount];
PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
mesh.getFaceSmoothingGroups().addAll(smooth);
return mesh;
}
So you can use 2 or 3 of them to create a coordinate system like this:
public Group createGrid(float size, float delta) {
if (delta < 1) {
delta = 1;
}
final PolygonMesh plane = createQuadrilateralMesh(size, size, (int) (size / delta), (int) (size / delta));
final PolygonMesh plane2 = createQuadrilateralMesh(size, size, (int) (size / delta / 5), (int) (size / delta / 5));
PolygonMeshView meshViewXY = new PolygonMeshView(plane);
meshViewXY.setDrawMode(DrawMode.LINE);
meshViewXY.setCullFace(CullFace.NONE);
PolygonMeshView meshViewXZ = new PolygonMeshView(plane);
meshViewXZ.setDrawMode(DrawMode.LINE);
meshViewXZ.setCullFace(CullFace.NONE);
meshViewXZ.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ = new PolygonMeshView(plane);
meshViewYZ.setDrawMode(DrawMode.LINE);
meshViewYZ.setCullFace(CullFace.NONE);
meshViewYZ.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
PolygonMeshView meshViewXY2 = new PolygonMeshView(plane2);
meshViewXY2.setDrawMode(DrawMode.LINE);
meshViewXY2.setCullFace(CullFace.NONE);
meshViewXY2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
PolygonMeshView meshViewXZ2 = new PolygonMeshView(plane2);
meshViewXZ2.setDrawMode(DrawMode.LINE);
meshViewXZ2.setCullFace(CullFace.NONE);
meshViewXZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewXZ2.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ2 = new PolygonMeshView(plane2);
meshViewYZ2.setDrawMode(DrawMode.LINE);
meshViewYZ2.setCullFace(CullFace.NONE);
meshViewYZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewYZ2.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
return new Group(meshViewXY, meshViewXY2, meshViewXZ, meshViewXZ2 /*, meshViewYZ, meshViewYZ2 */);
}
Note that I've duplicated the plane to mock a wider stroke every 5 lines.
Finally adding axes:
public Group getAxes(double scale) {
Cylinder axisX = new Cylinder(1, 200);
axisX.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS), new Translate(0, -100, 0));
axisX.setMaterial(new PhongMaterial(Color.RED));
Cylinder axisY = new Cylinder(1, 200);
axisY.getTransforms().add(new Translate(0, 100, 0));
axisY.setMaterial(new PhongMaterial(Color.GREEN));
Cylinder axisZ = new Cylinder(1, 200);
axisZ.setMaterial(new PhongMaterial(Color.BLUE));
axisZ.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS), new Translate(0, 100, 0));
Group group = new Group(axisX, axisY, axisZ);
group.getTransforms().add(new Scale(scale, scale, scale));
return group;
}
Now you have:
final Group axes = getAxes(0.5);
final Group grid = createGrid(200, 10);
final Sphere sphere = new Sphere(5);
sphere.getTransforms().add(new Translate(20, 15, 40));
Scene scene = new Scene(new Group(axes, grid, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
The total amount of nodes of this sample is 14.
Of course, it can be improved to add labels and many other features.
I'm using Core-Plot 0.9 and have a seemingly peculiar problem. I have an X-Y plot with labels on the y-axis. When the plot is initially displayed, the labels are correctly positioned on the tick marks (actually offset 1.0) to the left of the axis itself, which is correct.
Then, I select a new set of data to display using a UIPickerView to set the x-axis starting point and regenerate the plot. On this and all subsequent regenerations of the plot, the y-axis labels show up inside the plot (to the right of the axis) and not on the tick marks (actually shifted upwards a bit.
I have created what I think is enough room on the left hand side of the plot to contain the y-axis labels. In particular, I have code as follows:
graph.paddingLeft = 0.0;
graph.plotAreaFrame.paddingLeft = 25.0;
y.labelOffset = 1.0f;
y.labelAlignment = CPTAlignmentLeft;
Here is the correct representation of the y-axis labels on initial loading.
(source: msyapps.com)
Here is the incorrect representation of the y-axis labels after scrolling the picker view on the left and regenerating the plot.
(source: msyapps.com)
Are you changing the tickDirection property? That's what controls which side the labels are on.
I have a workable solution, but it violates the commonly recommended action for displaying the y-axis. The annotations in the code snippet below indicates where I think I deviated from conventions and where I think I have added a unique solution for fixing the y-axis in place on subsequent redrawing of plot.
#pragma mark - Choose returns to chart
- (IBAction)returnsToChart:(int)startPt andInteger:(int)endPt; {
contentArray1 = [[NSMutableArray alloc] initWithObjects:nil];
dataContent = [[NSMutableArray alloc] initWithObjects:nil];
int xPosition = 0;
for (int i=startPt; i<endPt+1; ++i) {
[dataContent addObject:[returnsAll objectAtIndex:i]];
id x = [NSNumber numberWithFloat:xPosition];
id y = [NSNumber numberWithFloat:[[returnsAll objectAtIndex:i] floatValue]];
if ([[returnsAll objectAtIndex:i] isEqualToString:#""]) {
[contentArray1 addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:x, #"x", #"", #"y", nil]];
}
else {
[contentArray1 addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:x, #"x", y, #"y", nil]]; }
xPosition = xPosition + 1;
}
dataForPlot = contentArray1;
quarterlyReturnView.frame = CGRectMake(0.0f, 8.0f, 320.0f, 240.0f);
graph = [[CPTXYGraph alloc] initWithFrame:CGRectZero];
quarterlyReturnView.hostedGraph = graph;
//paddingLeft leaves room for y-axis labels
//paddingBottom must be set at zero otherwise and the space that must be added in order for the bottom-most label text to show will be handled by extending the length of the y-axis downward by a little bit
graph.paddingLeft = 8.0;
graph.paddingTop = 5.0;
graph.paddingRight = 5.0;
graph.paddingBottom = 0.0;
//convention says that paddingLeft should be some number greater than zero
//convention says that paddingBottom should be some number greater than zero, but if greater than zero, then each subsequent redrawing of plot results in a label shift upwards by the non-zero amount
graph.plotAreaFrame.paddingLeft = 0.0;
graph.plotAreaFrame.paddingTop = 5.0;
graph.plotAreaFrame.paddingRight = 5.0;
graph.plotAreaFrame.paddingBottom = 0.0;
// Setup plot space
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
//this is where the magic happens...
//I tweaked the percentage adjustments for the location as a function of the number of data points along the x-axis that had to be plotted
//I settled on 8% plus and minus to provide room for the y-axis labels
//this percentage is expressed as -0.08*(datacontent.count-1.0) and 1.08*(datacontent.count-1.0)
//in other words, I put 8% on the left-hand side of the x-axis and made the whole x-axis 108% of the number of data points
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(-0.08 * (dataContent.count-1.0)) length:CPTDecimalFromFloat(1.08 * (dataContent.count-1.0))];
//Text styles
CPTMutableTextStyle *axisTitleTextStyle = [CPTTextStyle textStyle];
axisTitleTextStyle.fontName = #"Helvetica";
axisTitleTextStyle.fontSize = 9.0;
//Grid styles
CPTMutableLineStyle *majorGridLineStyle = [CPTLineStyle lineStyle];
majorGridLineStyle.lineWidth = 0.5f;
//Axis styles
CPTMutableLineStyle *axisLineStyle = [CPTLineStyle lineStyle];
axisLineStyle.lineColor = [CPTColor blueColor];
axisLineStyle.lineWidth = 1.0f;
float maxNum = -100.0;
float minNum = 100.0;
float testMaxNum = 0.0;
float testMinNum = 0.0;
for (int i=0; i<[dataContent count]; ++i) {
if ([[dataContent objectAtIndex:i] isEqualToString:#""]) { }
else {
testMaxNum = [[dataContent objectAtIndex:i] floatValue];
if (maxNum < testMaxNum) { maxNum = testMaxNum; }
testMinNum = [[dataContent objectAtIndex:i] floatValue];
if (minNum > testMinNum) { minNum = testMinNum; } }
}
int maxInt = (maxNum + 1.0) / 1.0;
float yMax = 1.0 * maxInt;
int minInt = (minNum - 1.0) / 1.0;
float yMin = 1.0 * minInt;
//here is where the y-axis is stretched a bit to allow the labels to show unclipped
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(yMin-0.2) length:CPTDecimalFromFloat(yMax-yMin+0.2)];
// Axes (x is horizontal; y is vertical)
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)graph.axisSet;
CPTXYAxis *x = axisSet.xAxis;
x.axisLineStyle = axisLineStyle;
x.labelOffset = 1.0f;
x.labelTextStyle = nil;
x.majorTickLength = 5.0f;
x.majorIntervalLength = CPTDecimalFromString(#"16");
x.orthogonalCoordinateDecimal = CPTDecimalFromString(#"0");
x.minorTicksPerInterval = 0;
x.minorTickLength = 0.0f;
x.tickDirection =CPTSignNone;
x.visibleRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0) length:CPTDecimalFromFloat(dataContent.count-1.0)];
CPTXYAxis *y = axisSet.yAxis;
y.axisLineStyle = axisLineStyle;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:0];
y.labelFormatter = formatter;
y.labelOffset = 2.0f;
y.labelAlignment = CPTAlignmentLeft;
y.labelTextStyle = axisTitleTextStyle;
y.tickDirection = CPTSignNegative;
y.majorTickLength = 3.0f;
y.majorIntervalLength = CPTDecimalFromString(#"2");
y.majorGridLineStyle = majorGridLineStyle;
//y.gridLinesRange is needed because the x-axis has been stretched 8% as described above
y.gridLinesRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromInt(0) length:CPTDecimalFromInt(dataContent.count-1)];
y.minorTicksPerInterval = 1;
y.minorTickLength = 2.0f;
y.orthogonalCoordinateDecimal = CPTDecimalFromString(#"0");
// Create plot area
CPTScatterPlot *boundLinePlot = [[CPTScatterPlot alloc] init];
CPTMutableLineStyle *lineStyle = [CPTMutableLineStyle lineStyle];
lineStyle.miterLimit = 1.0f;
lineStyle.lineWidth = 2.0f;
lineStyle.lineColor = [CPTColor blueColor];
boundLinePlot.dataLineStyle = lineStyle;
boundLinePlot.dataSource = self;
[graph addPlot:boundLinePlot];
[boundLinePlot reloadData];
}