I am using orson chart's Chart3D to make a surface plot, and for some reason the graph isn't properly coloring the gradient. The code is below:
#SuppressWarnings("serial")
Function3D function = new Function3D() {
#Override
public double getValue(double x, double z) {
double xKey = Math.round(x * 100) / 100;
double zKey = Math.round(z * 100) / 100;
if(plotValues.containsKey(new Point2D(xKey,zKey))) {
return plotValues.get(new Point2D(xKey,zKey));
} else {
return 0;
}
}
};
String xTitle = factorSweepComboBox.getSelectionModel().getSelectedItem();
String yTitle = outputComboBox.getSelectionModel().getSelectedItem();
String zTitle = factorSweep2ComboBox.getSelectionModel().getSelectedItem();
// Create surface plot
Chart3D chart = Chart3DFactory.createSurfaceChart(
"",
"",
function, xTitle, yTitle, zTitle);
XYZPlot xyzplot = (XYZPlot) chart.getPlot();
xyzplot.setDimensions(new Dimension3D(10, 10, 10));
ValueAxis3D xAxis = xyzplot.getXAxis();
xAxis.setRange(xLow, xUp);
ValueAxis3D zAxis = xyzplot.getZAxis();
zAxis.setRange(zLow, zUp);
ValueAxis3D yAxis = xyzplot.getYAxis();
yAxis.setRange(yLow, yUp);
SurfaceRenderer renderer = (SurfaceRenderer) xyzplot.getRenderer();
renderer.setColorScale(new GradientColorScale(new Range(yLow, yUp),
Color.BLUE, Color.YELLOW));
Chart3DViewer chartPanel = new Chart3DViewer(chart);
chartPane.getChildren().addAll(chartPanel);
plotValues is a hashmap mapping a (x,z) 2D point to a double y output value. xLow, xUp, etc. are range values and are all being set correctly. yLow and yUp are what I want them to be. However, when I run the code my surface is all one color, there is no gradient at all even though the key looks correct. I have also tried:
SurfaceRenderer renderer = new SurfaceRenderer(function);
renderer.setColorScale(new GradientColorScale(new Range(yLow, yUp),
Color.BLUE, Color.YELLOW));
xyzplot.setRenderer(renderer);
and the result is the same. Here is a link to a screenshot: http://i.imgur.com/F2iYFh1.png
I found the problem. It was due to my function only return real values on discrete x and z values, which works if the set of (x,z) matches the set in my own data hash. However, the surface renderer uses the mid-point between two "x" and "z"'s in the dataset when it plots the surface to determine the color, and because the middle point isn't in the hash it returns 0, making the whole surface one color.
Related
I have set of about 600 pairs of x and y value that should be plotted. They are read as doubles from a CSV file
void ChartView::getData4Series()
{
QFile file(QCoreApplication::applicationDirPath() + "/../saveData/" + m_videoPath + "/" + "SaveDisplace.csv");
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::information(0, "error", file.errorString());
}
QTextStream in(&file);
QString line;
QStringList fields;
QtCharts::QScatterSeries* series = new QtCharts::QScatterSeries;
m_seriesTy->clear();
m_seriesTz->clear();
bool checkforChar ;
while (!in.atEnd())
{
line = in.readLine();
fields = line.split(";");
fields[2].toDouble(&checkforChar);
if (checkforChar)
{
if (fields[2].toDouble() <= 100.0 && fields[2].toDouble() >= -5000.0)
{
if (fields[6].toDouble() > -100000.0)
{
m_seriesTy->append(fields[2].toDouble(), (fields[3].toDouble() - fields[6].toDouble()));
m_seriesTz->append(fields[2].toDouble(), (fields[4].toDouble() - fields[7].toDouble()));
}
}
}
}
file.close();
}
I set up the chart by setting the axis, given the limits and adding a further line (only 2 value pairs) as reference.
void ChartView::setZErrorCharts(QtCharts::QScatterSeries* series)
{
series->setMarkerSize(8);
QtCharts::QValueAxis *xAxis = new QtCharts::QValueAxis;
QtCharts::QValueAxis *yAxis = new QtCharts::QValueAxis;
xAxis->setRange(-5000, 0);
yAxis->setRange(-500, 500);
QtCharts::QLineSeries* lineSeries = new QtCharts::QLineSeries;
lineSeries->append(qreal(0), qreal(0));
lineSeries->append(qreal(-5000), qreal(0));
m_chartTzError = new QtCharts::QChart();
m_chartTzError->setAxisX(xAxis);
m_chartTzError->setAxisY(yAxis);
m_chartTzError->setTitle("tz error ");
m_chartTzError->legend()->hide();
m_chartTzError->addSeries(series);
m_chartTzError->addSeries(lineSeries);
}
What I can see is that the value of axis don't fit to the actual values of the plotted one.
I also plotted the values as labels and they also differ from the x and y axis values.
How is the scaling of the axis arranged. I tried to mess aroud with the order, setting the range of the axis before adding the data series and vise versa.
The values marked by the red circle should be around 0 but are at approximatley -200.
Any idea how to fix this.
Qt reference doesn't seem to help.
I had the same problem and solved it this way:
You create the axes which you need first. For example:
QLogValueAxis *axisX = new QLogValueAxis();
axisX->setTitleText("Frequency [Hz]");
axisX->setRange(200, 8000);
axisX->setMinorGridLineVisible(true);
axisX->setMinorTickCount(10);
chart->addAxis(axisX, Qt::AlignBottom);
QValueAxis *axisY = new QValueAxis();
axisY->setRange(20, 150);
axisY->setTitleText("dB");
chart->addAxis(axisY, Qt::AlignLeft);
Then, you create your series':
QLineSeries *series = new QLineSeries(chart);
series->append(x,y);
Add the series to your chart:
chart->addSeries(series);
Finally, attach the right axis to the series:
series->attachAxis(axisX);
series->attachAxis(axisY);
When you plot it now:
ui->chart_view->setChart(chart);
Everything should be in the right scale.
I think the decisive step is to attach the right corresponding axis when you add the series to the chart.
I am using three.js to create a simple 3d vector environment. I am using lines to represent all 3 vector compontens x, y, z and a line for the final vector representation. Problem is that setting the width of a line is not working in Windows. The workaround that I try to implement is placing a cylinder onto the line (see red object in image below).
That is my current result:
As you see I am not able to rotate the cylinder to the correct position.
I faced the problem that the rotation center of the cylinder is in the middle of the object, so I moved the rotation point to the beginning of the cylinder. But still, rotation is not working correctly. I guess, the rotations around the axis influence each other.
Here is the code:
// VEKTOR
var vektor = {};
vektor._x = 2;
vektor._y = 1.5;
vektor._z = 1;
vektor._length = Math.sqrt(vektor._x*vektor._x + vektor._y*vektor._y + vektor._z*vektor._z);
// CYLINDER
var cyl_material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
// cylinder which is our line that represents the vector
var cyl_width = 0.025; // default line width
var cyl_height = vektor._length;
// THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)
var cylGeometry = new THREE.CylinderGeometry(cyl_width, cyl_width, cyl_height, 20, 1, false);
// translate the cylinder geometry so that the desired point within the geometry is now at the origin
// https://stackoverflow.com/questions/12746011/three-js-how-do-i-rotate-a-cylinder-around-a-specific-point
cylGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, cyl_height/2, 0 ) );
var cylinder = new THREE.Mesh(cylGeometry, cyl_material);
updateCylinder();
scene.add( cylinder );
And the function updateCylinder trys to do the rotation.
function updateCylinder() {
// ... stuff, then:
cylinder.rotation.x = Math.atan2(vektor._z,vektor._y);
cylinder.rotation.y = 0.5*Math.PI+Math.atan2(vektor._x,vektor._z);
cylinder.rotation.z = Math.atan2(vektor._x,vektor._y);
}
Here is the current demo: http://www.matheretter.de/3d/vektoren/komponenten/
What am i doing wrong with the rotation? How to implement it so that the cylinder is following the vector line?
Thanks for your help.
If you want to transform a cylinder so that one end is at the origin and the other end points toward a specific point, here is the pattern you can follow:
First, transform your geometry so one end of the cylinder is at the origin, and the other end (the top) is on the positive z-axis.
var geometry = new THREE.CylinderGeometry( 0, 1, length, 8, 1, true );
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, length / 2, 0 ) );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
Then create your mesh, and call the lookAt() method:
var mesh = new THREE.Mesh( geometry, material );
mesh.lookAt( point );
three.js r.67
In this answer to my recent question, there is some code that draws a graph, but I can't manage to edit it into something that accepts any list of points as a parameter.
I'd like the Drawing method to accept these parameters:
List of Vector2, Point or VertexPositionColor, I can work with whichever.
Offset for the whole graph
These optional requirements would be appreciated:
Color that may override VertexPositionColor's color and apply to all points.
Size of the graph, so it can be shrunk or expanded, either as Vector2 as multiplier, or Point as target size. Maybe even combine this with offset in Rectangle.
And if it's possible, I'd like to have it all in a class, so graphs can be used separately from each other, each with its own Effect.world matrix, etc.
Here is that code (by Niko Drašković):
Matrix worldMatrix;
Matrix viewMatrix;
Matrix projectionMatrix;
BasicEffect basicEffect;
VertexPositionColor[] pointList;
short[] lineListIndices;
protected override void Initialize()
{
int n = 300;
//GeneratePoints generates a random graph, implementation irrelevant
pointList = new VertexPositionColor[n];
for (int i = 0; i < n; i++)
pointList[i] = new VertexPositionColor() { Position = new Vector3(i, (float)(Math.Sin((i / 15.0)) * height / 2.0 + height / 2.0 + minY), 0), Color = Color.Blue };
//links the points into a list
lineListIndices = new short[(n * 2) - 2];
for (int i = 0; i < n - 1; i++)
{
lineListIndices[i * 2] = (short)(i);
lineListIndices[(i * 2) + 1] = (short)(i + 1);
}
worldMatrix = Matrix.Identity;
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
basicEffect.VertexColorEnabled = true; //important for color
base.Initialize();
}
And the drawing method:
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
PrimitiveType.LineList,
pointList,
0,
pointList.Length,
lineListIndices,
0,
pointList.Length - 1
);
}
The Graph class that does the requested can be found here.About 200 lines of code seemed too much to paste here.
The Graph is drawn by passing a list of floats (optionally with colors) to its Draw(..) method.
Graph properties are:
Vector2 Position - the bottom left corner of the graph
Point Size - the width (.X) and height (.Y) of the graph. Horizontally, values will be distributed to exactly fit the width. Vertically, all values will be scaled with Size.Y / MaxValue.
float MaxValue - the value which will be at the top of the graph. All off the chart values (greater than MaxValue) will be set to this value.
GraphType Type - with possible values GraphType.Line and GraphType.Fill, determines if the graph will be drawn line only, or bottom filled.
The graph is drawn with a line list / triangle strip.
I can't figure out how to perform matrix rotation using Quaternion while taking into account pivot position in OpenGL.What I am currently getting is rotation of the object around some point in the space and not a local pivot which is what I want.
Here is the code [Using Java]
Quaternion rotation method:
public void rotateTo3(float xr, float yr, float zr) {
_rotation.x = xr;
_rotation.y = yr;
_rotation.z = zr;
Quaternion xrotQ = Glm.angleAxis((xr), Vec3.X_AXIS);
Quaternion yrotQ = Glm.angleAxis((yr), Vec3.Y_AXIS);
Quaternion zrotQ = Glm.angleAxis((zr), Vec3.Z_AXIS);
xrotQ = Glm.normalize(xrotQ);
yrotQ = Glm.normalize(yrotQ);
zrotQ = Glm.normalize(zrotQ);
Quaternion acumQuat;
acumQuat = Quaternion.mul(xrotQ, yrotQ);
acumQuat = Quaternion.mul(acumQuat, zrotQ);
Mat4 rotMat = Glm.matCast(acumQuat);
_model = new Mat4(1);
scaleTo(_scaleX, _scaleY, _scaleZ);
_model = Glm.translate(_model, new Vec3(_pivot.x, _pivot.y, 0));
_model =rotMat.mul(_model);//_model.mul(rotMat); //rotMat.mul(_model);
_model = Glm.translate(_model, new Vec3(-_pivot.x, -_pivot.y, 0));
translateTo(_x, _y, _z);
notifyTranformChange();
}
Model matrix scale method:
public void scaleTo(float x, float y, float z) {
_model.set(0, x);
_model.set(5, y);
_model.set(10, z);
_scaleX = x;
_scaleY = y;
_scaleZ = z;
notifyTranformChange();
}
Translate method:
public void translateTo(float x, float y, float z) {
_x = x - _pivot.x;
_y = y - _pivot.y;
_z = z;
_position.x = _x;
_position.y = _y;
_position.z = _z;
_model.set(12, _x);
_model.set(13, _y);
_model.set(14, _z);
notifyTranformChange();
}
But this method in which I don't use Quaternion works fine:
public void rotate(Vec3 axis, float angleDegr) {
_rotation.add(axis.scale(angleDegr));
// change to GLM:
Mat4 backTr = new Mat4(1.0f);
backTr = Glm.translate(backTr, new Vec3(_pivot.x, _pivot.y, 0));
backTr = Glm.rotate(backTr, angleDegr, axis);
backTr = Glm.translate(backTr, new Vec3(-_pivot.x, -_pivot.y, 0));
_model =_model.mul(backTr);///backTr.mul(_model);
notifyTranformChange();
}
It seems to me you take into account the back and forth translation before and after the rotation already. Why that final call of translateTo?
Besides, when you rotate, a pure rotation is always meant around the origin. So if you want a rotation around your pivot point. I'd exptect to translate your pivot point to the origin, then rotate, then translate back to the pivot would be the right thing to do. Therefore, I'd expect your code to look like this:
_model = Glm.translate(_model, new Vec3(-_pivot.x, -_pivot.y, 0));
_model =rotMat.mul(_model);//_model.mul(rotMat); //rotMat.mul(_model);
_model = Glm.translate(_model, new Vec3(_pivot.x, _pivot.y, 0));
and without the call translateTo(_x, _y, _z);. Also, can you confirm that the rotation part already does what it supposed to? You can check this by comparing rotMat with Glm.rotate(new Mat4(1.0f), angleDegr, axis). They should be the same for the same rotation.
A quaternion describes only a rotation. As a result how do you want to rotate something around a pivot point with only a quaternion?
The minimum that you need is a R3 vector and a quaternion. With only one level of transformation you first roatate the object and then move it there.
If you want to create a matrix you first create the ration matrix and then add the translation unaltered.
If you want to just call glTranslate and glRotate (or glMultMatrix) you would first call glTranslate and then glRoatate.
Edit:
If you are not rendering and just want to know where each vertex is:
Vector3 newVertex = quat.transform(oldVertex) + translation;
I have a program that I'm making with others and I ran into a problem. I'm working on adding in polygon models into our scene in an XNA window. I have that part complete. I also have bounding spheres(I know I tagged as bounding-box but there is no bounding sphere tag) drawing around each polygon. My problem is when I move the polygons around the 3D space the bounding spheres move twice as much as the polygons. I imagine its something within my polygon matrices that I use to create the bounding sphere that makes it move twice as much but that is only speculation.
So just to clarify I'll give you an example of my problem. If I hold down D to move a polygon along the X axis. (model.position.X--;) The polygon moves as expected to but the bounding sphere around the polygon moves twice as much. Thanks for the help guys!
Here is how I draw the models and the bounding spheres:
public void Draw(Matrix view, Matrix projection, bool drawBoundingSphere)
{
Matrix translateMatrix = Matrix.CreateTranslation(position);
Matrix worldMatrix = translateMatrix * Matrix.CreateScale(scaleRatio);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = worldMatrix * modelAbsoluteBoneTransforms[mesh.ParentBone.Index];
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;
}
mesh.Draw();
if (drawBoundingSphere)
{
// the mesh's BoundingSphere is stored relative to the mesh itself.
// (Mesh space). We want to get this BoundingSphere in terms of world
// coordinates. To do this, we calculate a matrix that will transform
// from coordinates from mesh space into world space....
Matrix world = modelAbsoluteBoneTransforms[mesh.ParentBone.Index] * worldMatrix;
// ... and then transform the BoundingSphere using that matrix.
BoundingSphere sphere = BoundingSphereRenderer.TransformBoundingSphere(mesh.BoundingSphere, world);
// now draw the sphere with our renderer
BoundingSphereRenderer.Draw(sphere, view, projection);
}
}
And here is the BoundingSphereRenderer Code:
private static VertexBuffer vertexBuffer;
private static BasicEffect effect;
private static int lineCount;
public static void Initialize(GraphicsDevice graphicsDevice, int sphereResolution)
{
// create our effect
effect = new BasicEffect(graphicsDevice);
effect.LightingEnabled = false;
effect.VertexColorEnabled = true;
// calculate the number of lines to draw for all circles
lineCount = (sphereResolution + 1) * 3;
// we need two vertices per line, so we can allocate our vertices
VertexPositionColor[] vertices = new VertexPositionColor[lineCount * 2];
// compute our step around each circle
float step = MathHelper.TwoPi / sphereResolution;
// used to track the index into our vertex array
int index = 0;
//create the loop on the XY plane first
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0f), Color.Blue);
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle + step), (float)Math.Sin(angle + step), 0f), Color.Blue);
}
//next on the XZ plane
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle), 0f, (float)Math.Sin(angle)), Color.Red);
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle + step), 0f, (float)Math.Sin(angle + step)), Color.Red);
}
//finally on the YZ plane
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3(0f, (float)Math.Cos(angle), (float)Math.Sin(angle)), Color.Green);
vertices[index++] = new VertexPositionColor(new Vector3(0f, (float)Math.Cos(angle + step), (float)Math.Sin(angle + step)), Color.Green);
}
// now we create the vertex buffer and put the vertices in it
vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices);
}
public static void Draw(this BoundingSphere sphere, Matrix view, Matrix projection)
{
if (effect == null)
throw new InvalidOperationException("You must call Initialize before you can render any spheres.");
// set the vertex buffer
effect.GraphicsDevice.SetVertexBuffer(vertexBuffer);
// update our effect matrices
effect.World = Matrix.CreateScale(sphere.Radius) * Matrix.CreateTranslation(sphere.Center);
effect.View = view;
effect.Projection = projection;
// draw the primitives with our effect
effect.CurrentTechnique.Passes[0].Apply();
effect.GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, lineCount);
}
public static BoundingSphere TransformBoundingSphere(BoundingSphere sphere, Matrix transform)
{
BoundingSphere transformedSphere;
// the transform can contain different scales on the x, y, and z components.
// this has the effect of stretching and squishing our bounding sphere along
// different axes. Obviously, this is no good: a bounding sphere has to be a
// SPHERE. so, the transformed sphere's radius must be the maximum of the
// scaled x, y, and z radii.
// to calculate how the transform matrix will affect the x, y, and z
// components of the sphere, we'll create a vector3 with x y and z equal
// to the sphere's radius...
Vector3 scale3 = new Vector3(sphere.Radius, sphere.Radius, sphere.Radius);
// then transform that vector using the transform matrix. we use
// TransformNormal because we don't want to take translation into account.
scale3 = Vector3.TransformNormal(scale3, transform);
// scale3 contains the x, y, and z radii of a squished and stretched sphere.
// we'll set the finished sphere's radius to the maximum of the x y and z
// radii, creating a sphere that is large enough to contain the original
// squished sphere.
transformedSphere.Radius = Math.Max(scale3.X, Math.Max(scale3.Y, scale3.Z));
// transforming the center of the sphere is much easier. we can just use
// Vector3.Transform to transform the center vector. notice that we're using
// Transform instead of TransformNormal because in this case we DO want to
// take translation into account.
transformedSphere.Center = Vector3.Transform(sphere.Center, transform);
return transformedSphere;
}