How can I get transform matrix for QQuickItem? - qt

I worked for a long time with QGraphicsItem and it has transform() function. Now I wont to do same thing with QQuickItem but unfortunately it misses transform(). So my question - how can I get transform matrix for QQuickItem?

Actually the QQuickItem provides the transform() method, however it returns the list of all transformations assigned to given item. It is because multiple transformations can be assigned to a single Item. The return type of QQuickItem::transform is QQmlListProperty<QQuickTransform> — it is a wrapper to QML list<Transform> type (see documentation for Item). It can be iterated over, yielding QQuickTransform * elements. QQuickTransform is a base class for a transformation that provides a virtual method applyTo taking a QMatrix4x4 * argument and applying the transformation upon it.
The QML allows instantiating several QQuickTransform subclasses (for translation, rotation and scale) and user is allowed to defined custom transformations (eg. for skew).
To obtain a single transformation matrix you need, you have to start with identity matrix and sequentially apply all the transformations of given QQuickItem.
QMatrix4x4 transformOfItem(QQuickItem *item)
{
QQmlListProperty transformations = item->transform();
const int count = transformations.count(&transformations);
// Prepare result structure, it will be default-initialized to be an identity matrix
QMatrix4x4 transformMatrix;
// Apply sequentially all transformation from the item
for(int i = 0; i applyTo(&transformMatrix);
}
return transformMatrix;
}
Note that the function returns a tranformation matrix as QMatrix4x4 — it is more than old QTransform that was based on 3x3 transformation matrix, so it cannot be converted without loss. If you want, you may use QMatrix4x4::toAffine to get the QMatrix (3x3) and use it to create QTransform object. However, if your QQuickItem transformations contain non-affinic elements, they will be lost.
Edit
There's one more thing to note: the method I posted works only for transformations defined by assigning to transform property. It does not check for scale and rotation properties. If you use them, you should check their values with appropriate QQuickItem methods and adjust returned matrix to include these two additional tranformations.

Here's a correct solution, based on the code provided by Michael earlier, but fixed to work actually, so you don't have to spend 20 minutes figuring out how to use QQmlListProperty
QMatrix4x4 YourQQuickItem::get_model_matrix() {
QMatrix4x4 result;
// Compose model matrix from our transform properties in the QML
QQmlListProperty<QQuickTransform> transformations = transform();
const int count = transformations.count(&transformations);
for (int i=0; i<count; i++) {
QQuickTransform *transform = transformations.at(&transformations, i);
transform->applyTo(&result);
}
return result;
}
In my use case I use this to get the model matrix for my object, then multiply together with view and projection matrixes to calculate the model-view-projection matrix.

The QSGTransformNode class implements transformations in the scene graph. In updatePaintNode function, argument updatePaintNodeData provides a pointer to the QSGTransformNode associated with this QQuickItem.
QSGNode *MyQuickItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data)
{
QSGTransformNode *transformNode = data->transformNode;
qDebug() << transformNode->matrix();

Related

Given 3 or more numbers or vectors, how do I interpolate between them based on a percentage?

I want to know the most basic math principles I need to interpolate a value between 3 or more other values, based on a linear percentage; as it would be applicable in programming.
For example, say I have "0", "100", "200", and I want the number that's at "50%". The math would then return something like "100" because 100 is at 50%.
Another example: I have 3 points somewhere in 3D space. If I do "75%" then the result would be a point that is exactly halfway between point 2 and 3, or if I do "25%" then it'll be half-way between 1 and 2.
Game engines like Unity use something like this for blending between multiple animations on a character, for another example.
What I've brainstormed so far is that I would somehow take the input value and find whatever the 2 neighboring "points" are closest to it (much harder in 3D or 2d space but manageable in 1d), then simply lerp between those two- but that requires me to figure out what percentage both of those points are at individually, and remap from "0 to 100%" to "A% to B%". I think it would work but It seems kind of complicated to me.
If possible, I'd like answers to include a C# example or language-agnostic psuitocode just so I can understand the math.
simple example for scalar float objects using piecewise linear interpolation:
int n=3; // number of your objects
float x[n]={ 0.5,2.0,10.0 }; // your objects
float get_object(float t) // linearly interpolate objects x[] based in parameter t = <0,1>, return value must be the same type as your objects
{
int ix;
float x0,x1; // the same type as your objects
// get segment ix and parameter t
t*=n; ix=floor(t); t-=ix;
// get closest known points x0,x1
x0=x[ix]; ix++;
if (ix<n) x1=x[ix]; else return x0;
// interpolate
return x0+(x1-x0)*t;
}
so if t=0 it returns first object in the x[] if it is t=1 is returns last and anything in between is linearly interpolated ... The idea is just to multiply our t by number of segments or point (depend on how you handle edge cases) which integer part of the result will give us index of closest 2 objects to our wanted one and then the fractional part of multiplied t will give us directly interpolation parameter in range <0,1> between the two closest points...
In case you objects are not with the same weight or are not uniformly sampled then you need to add interpolation with weights or use higher order polynomial (quadratic,cubic,...).
You can use this for "any" type T of objects you just have to implement operations T+T , T-T and T*float if they are not present.
If your gameObjects is at the same line try this code.
public Transform objStart;
public Transform objEnd;
public Transform square;
public float distance;
//percent .5 means 50%
[Range(0f,1f)]
public float percent;
public Vector3 distancePercentPosition;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//get distance between two object
distance = Vector3.Magnitude(objEnd.position - objStart.position);
//get position based on percent;
distancePercentPosition = (objEnd.position - objStart.position).normalized * percent * distance;
square.position = objStart.position + distancePercentPosition;
}
once you get the position between lines you can now map your gameobject in each position based on percent.

Getting a part of a QMap as a QVector

I have some elements in a QMap<double, double> a-element. Now I want to get a vector of some values of a. The easiest approach would be (for me):
int length = x1-x0;
QVector<double> retVec;
for(int i = x0; i < length; i++)
{
retVec.push_back(a.values(i));
}
with x1 and x0 as the stop- and start-positions of the elements to be copied. But is there a faster way instead of using this for-loop?
Edit: With "faster" I mean both faster to type and (not possible, as pointed out) a faster execution. As it has been pointed out, values(i) is not working as expected, thus I will leave it here as pseudo-code until I found a better_working replacement.
Maybe this works:
QVector<double>::fromList(a.values().mid(x0, length));
The idea is to get all the values as a list of doubles, extract the sublist you are interested in, thus create a vector from that list by means of an already existent static method of QVector .
EDIT
As suggested in the comments and in the updated question, it follows a slower to type but faster solution:
QVector<double> v{length};
auto it = a.cbegin()+x0;
for(auto last = it+length; it != last; it++) {
v.push_back(it.value());
}
I assume that x0 and length take care of the actual length of the key list, so a.cbegin()+x0 is valid and it doesn't worth to add the guard it != a.cend() as well.
Try this, shouldn work, haven't tested it:
int length = x1-x0;
QVector<double> retVec;
retVec.reserve(length); // reserve to avoid reallocations
QMap<double, double>::const_iterator i = map.constBegin();
i += x0; // increment to range start
while (length--) retVec << i++.value(); // add value to vector and advance iterator
This assumes the map has actually enough elements, thus the iterator is not tested before use.

Adding one to a vector

I am trying to translate c code into MATLAB, and I have come across some code that I don't understand. Specifically, there is a variable defined as:
static float *lpfdata;
This gets assigned during a function call to:
envelope_old(&fdata[0], lpfdata, winlength, samprate, BW);
Which accepts input as:
void envelope_old (float *fdata, float *lpfdata, int nsamps, int samprate,
float cutoff)
Within envelope_old, lpfdata is referenced as a vector, being assigned values in a loop in the format "lpfdata[i] = ..." where i is the index variable in the loop.
Later, a function call in the format:
downsample( lpfdata+1, dwndata, winlength, downby);
is called. What does the +1 mean in this instance?
When dealing with a pointer, lpfdata[n] and lpfdata+n are the same - they both add n * sizeof(*lpfdata) to the raw pointer and access the memory at that address.
In this case, lpfdata points to elements of type float, so sizeof(*lpfdata) == sizeof(float)

Usage of Map and Translate Functions in Processing

New to Processing working on understanding this code:
import com.onformative.leap.LeapMotionP5;
import java.util.*;
LeapMotionP5 leap;
LinkedList<Integer> values;
public void setup() {
size(800, 300);
frameRate(120); //Specifies the number of frames to be displayed every second
leap = new LeapMotionP5(this);
values = new LinkedList<Integer>();
stroke(255);
}
int lastY = 0;
public void draw() {
**translate(0, 180)**; //(x, y, z)
background(0);
if (values.size() >= width) {
values.removeFirst();
}
values.add((int) leap.getVelocity(leap.getHand(0)).y);
System.out.println((int) leap.getVelocity(leap.getHand(0)).y);
int counter = 0;
** for (Integer val : values)** {
**val = (int) map(val, 0, 1500, 0, height);**
line(counter, val, counter - 1, lastY);
point(counter, val);
lastY = val;
counter++;
}
** line(0, map(1300, 0, 1500, 0, height), width, map(1300, 0, 1500, 0, height)); //(x1, y1, x2, y2)**
}
It basically draw of graph of movement detected on the y axis using the Leap Motion sensor. Output looks like this:
I eventually need to do something similar to this that would detect amplitude instead of velocity simultaneously on all 3 axis instead of just the y.
The use of Map and Translate are whats really confusing me. I've read the definitions of these functions on the Processing website so I know what they are and the syntax, but what I dont understand is the why?! (which is arguably the most important part.
I am asking if someone can provide simple examples that explain the WHY behind using these 2 functions. For instance, given a program that needs to do A, B, and C, with data foo, y, and x, you would use Map or Translate because A, B, and C.
I think programming guides often overlook this important fact but to me it is very important to truly understanding a function.
Bonus points for explaining:
for (Integer val : values) and LinkedList<Integer> values; (cant find any documentation on the processing website for these)
Thanks!
First, we'll do the easiest one. LinkedList is a data structure similar to ArrayList, which you may be more familiar with. If not, then it's just a list of values (of the type between the angle braces, in this case integer) that you can insert and remove from. It's a bit complicated on the inside, but if it doesn't appear in the Processing documentation, it's a safe bet that it's built into Java itself (java documentation).
This line:
for (Integer val : values)
is called a "for-each" or "foreach" loop, which has plenty of very good explanation on the internet, but I'll give a brief explanation here. If you have some list (perhaps a LinkedList, perhaps an ArrayList, whatever) and want to do something with all the elements, you might do something like this:
for(int i = 0; i < values.size(); i++){
println(values.get(i)); //or whatever
println(values.get(i) * 2);
println(pow(values.get(i),3) - 2*pow(values.get(i),2) + values.get(i));
}
If you're doing a lot of manipulation with each element, it quickly gets tedious to write out values.get(i) each time. The solution would be to capture values.get(i) into some variable at the start of the loop and use that everywhere instead. However, this is not 100% elegant, so java has a built-in way to do this, which is the for-each loop. The code
for (Integer val : values){
//use val
}
is equivalent to
for(int i = 0; i < values.size(); i++){
int val = values.get(i);
//use val
}
Hopefully that makes sense.
map() takes a number in one linear system and maps it onto another linear system. Imagine if I were an evil professor and wanted to give students random grades from 0 to 100. I have a function that returns a random decimal between 0 and 1, so I can now do map(rand(),0,1,0,100); and it will convert the number for me! In this example, you could also just multiply by 100 and get the same result, but it is usually not so trivial. In this case, you have a sensor reading between 0 and 1500, but if you just plotted that value directly, sometimes it would go off the screen! So you have to scale it to an appropriate scale, which is what that does. 1500 is the max that the reading can be, and presumably we want the maximum graphing height to be at the edge of the screen.
I'm not familiar with your setup, but it looks like the readings can be negative, which means that they might get graphed off the screen, too. The better solution would be to map the readings from -1500,1500 to 0,height, but it looks like they chose to do it a different way. Whenever you call a drawing function in processing (eg point(x,y)), it draws the pixels at (x,y) offset from (0,0). Sometimes you don't want it to draw it relative to (0,0), so the translate() function allows you to change what it draws things relative against. In this case, translating allows you to plot some point (x,0) somewhere in the middle of the screen, rather than on the edge.
Hope that helps!

QMap Memory Error

I am doing one project in which I define a data types like below
typedef QVector<double> QFilterDataMap1D;
typedef QMap<double, QFilterDataMap1D> QFilterDataMap2D;
Then there is one class with the name of mono_data in which i have define this variable
QFilterMap2D valid_filters;
mono_data Scan_data // Class
Now i am reading one variable from a .mat file and trying to save it in to above "valid_filters" QMap.
Qt Code: Switch view
for(int i=0;i<1;i++)
{
for(int j=0;j<1;j++)
{
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
The transferring is done successfully but then it gives run-time error
Windows has triggered a breakpoint in SpectralDataCollector.exe.
This may be due to a corruption of the heap, and indicates a bug in
SpectralDataCollector.exe or any of the DLLs it has loaded.
The output window may have more diagnostic information
Can anyone help in solving this problem. It will be of great help to me.
Thanks
Different issues here:
1. Using double as key type for a QMap
Using a QMap<double, Foo> is a very bad idea. the reason is that this is a container that let you access a Foo given a double. For instance:
map[0.45] = foo1;
map[15.74] = foo2;
This is problematic, because then, to retrieve the data contained in map[key], you have to test if key is either equal, smaller or greater than other keys in the maps. In your case, the key is a double, and testing if two doubles are equals is not a "safe" operation.
2. Using an int as key while you defined it was double
Here:
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
i is an integer, and you said it should be a double.
3. Your loop only test for (i,j) = (0,0)
Are you aware that
for(int i=0;i<1;i++)
{
for(int j=0;j<1;j++)
{
Scan_Data.valid_filters[i][j]=valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
is equivalent to:
Scan_Data.valid_filters[0][0]=valid_filters[0][0];
printf("\nValid_filters=%f",Scan_Data.valid_filters[0][0]);
?
4. Accessing a vector with operator[] is not safe
When you do:
Scan_Data.valid_filters[i][j]
You in fact do:
QFilterDataMap1D & v = Scan_Data.valid_filters[i]; // call QMap::operator[](double)
double d = v[j]; // call QVector::operator[](int)
The first one is safe, and create the entry if it doesn't exist. The second one is not safe, the jth element in you vector must already exist otherwise it would crash.
Solution
It seems you in fact want a 2D array of double (i.e., a matrix). To do this, use:
typedef QVector<double> QFilterDataMap1D;
typedef QVector<QFilterDataMap1D> QFilterDataMap2D;
Then, when you want to transfer one in another, simply use:
Scan_Data.valid_filters = valid_filters;
Or if you want to do it yourself:
Scan_Data.valid_filters.clear();
for(int i=0;i<n;i++)
{
Scan_Data.valid_filters << QFilterDataMap1D();
for(int j=0;j<m;j++)
{
Scan_Data.valid_filters[i] << valid_filters[i][j];
printf("\nValid_filters=%f",Scan_Data.valid_filters[i][j]);
}
}
If you want a 3D matrix, you would use:
typedef QVector<QFilterDataMap2D> QFilterDataMap3D;

Resources