QML: SplineSeries without ChartView - qml

Is it possible to draw a SplineSeries like in this example, but there'd be just the spline, no other components of the chart view (axis, background etc.)? Or do I have to use canvas or something similar? In that case what would be the best possibility here? I have my x, y values, so if I just could place the SplineSeries itself to my layout like this, but that's probably not possible:
ColumnLayout {
SplineSeries {
name: "spline"
XYPoint { x: 0; y: 0.0 }
XYPoint { x: 1.1; y: 3.2 }
XYPoint { x: 1.9; y: 2.4 }
XYPoint { x: 2.1; y: 2.1 }
}
}

Related

How to draw points of different colours in XCB

I'm trying to draw points on the window, using the PolyPoint XCB request.
Note that I'm using the crate "xcb" in Rust.
Here is my function :
fn set_pixels(&mut self, pixels: Vec<(usize, usize, u32)>) {
self.connection.send_request(
&x::PolyPoint {
coordinate_mode: x::CoordMode::Origin,
drawable: x::Drawable::Window(self.handle.unwrap()),
gc: self.gc.unwrap(),
points: pixels.into_iter().map(|(x, y, colour)| {
x::Point {
x: x as i16,
y: y as i16,
}
})
.collect::<Vec<x::Point>>().as_slice(),
}
);
}
At first, I'm not sure if this part is the easiest way to get a slice of x::Point from the vector :
pixels.into_iter().map(|(x, y, colour)| {
x::Point {
x: x as i16,
y: y as i16,
}
})
.collect::<Vec<x::Point>>().as_slice(),
Well, as we can see, we got a "colour" for each pixel, and I would like to use x::PolyPoint with a colour for each point I want to draw.
I know I can use ChangeGc to set a drawing colour :
self.connection.send_request(
&x::ChangeGc {
gc: self.gc.unwrap(),
value_list: &[
x::Gc::Foreground(/* hex colour */),
],
}
);
But this would set the same colour for all the pixels.
How can I use "PolyPoint" to set pixels of different colours ? Without passing by a loop that would ChangeGC then just after use PolyPoint for one single pixel (this solution is too slow).
Earlier, I was doing a loop calling this function, to set pixels one by one. But this is too slow :
fn set_pixel(&mut self, x: usize, y: usize, hex_colour: u32) {
self.connection.send_request(
&x::ChangeGc {
gc: self.gc.unwrap(),
value_list: &[
x::Gc::Foreground(hex_colour),
],
}
);
self.connection.send_request(
&x::PolyPoint {
coordinate_mode: x::CoordMode::Origin,
drawable: x::Drawable::Window(self.handle.unwrap()),
gc: self.gc.unwrap(),
points: &[
x::Point {
x: x as i16,
y: y as i16,
}
]
}
)
}
You cannot set different colors for a single drawing request in X11. I think this is not even possible with the RENDER extension. So, all the options you have are the ones you or others already mention.
Well, one more idea: If you usually have few different colors, you could group things by color. Your input seems to be Vec<(usize, usize, u32)>. You could transform this into a HashMap<u32, Vec<(usize,usize)>> and then use that to draw all pixels of a single color at once. Of course, this does not make sense if you expect few pixels of each color.
I'm now working with the double-buffering method.
A simple way is to draw on a x::Pixmap object, and then create an update() function for the window's structure :
/// Copies the `self.pixmap` area to the window.
fn update(&mut self) {
self.connection.send_and_check_request(
&x::CopyArea {
src_drawable: x::Drawable::Pixmap(self.pixmap.unwrap()),
dst_drawable: x::Drawable::Window(self.window.unwrap()),
gc: self.gc.unwrap(),
src_x: 0,
src_y: 0,
dst_x: 0,
dst_y: 0,
width: self.width as u16,
height: self.height as u16,
}
)
.expect("double buffering: unable to copy the buffer to the window");
}
Here is my set_pixels method (renamed to draw_points :
fn draw_points(&mut self, coordinates: &Vec<(isize, isize)>, colour: u32) {
self.change_draw_colour(colour);
// Creates an `x::Point` vector.
let points = coordinates.into_iter().map(|coordinate: &(isize, isize)| {
x::Point {
x: coordinate.0 as i16,
y: coordinate.1 as i16,
}
})
.collect::<Vec<x::Point>>();
self.connection.send_and_check_request(
&x::PolyPoint {
coordinate_mode: x::CoordMode::Origin,
drawable: x::Drawable::Pixmap(self.pixmap.unwrap()),
gc: self.gc.unwrap(),
points: points.as_slice(),
}
)
.expect("unable to draw points on the pixmap");
}
For external reasons that I won't go into, I'm not directly using a vector of x::Point, there is why I transform my coordinates value to a Vec<x::Point>.
For optimisation, I'm also saving the previous colour to avoid changing colour to the same colour :
fn change_draw_colour(&mut self, colour: u32) {
if self.previous_colour == Some(colour) {
return;
}
self.connection.send_and_check_request(
&x::ChangeGc {
gc: self.gc.unwrap(),
value_list: &[
x::Gc::Foreground(colour),
],
}
)
.expect("unable to change the graphics context colour");
self.previous_colour = Some(colour);
}

how to zoom in to a sprite in bevy

I wanted to implement zooming in my 2d bevy game. After some code browsing I found out that Camera2dBundle uses OrthographicProjection by default and can not zoom in as required.
I tried using Camera3dBundle which does define projection: PerspectiveProjection by default but my sprite seems to disappear from the scene.
Could you give me some pointers to what I'm doing wrong? I have included some test code below.
Thanks
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(zoom_in)
.run();
}
fn setup(
mut commands: Commands
) {
commands.spawn_bundle(Camera3dBundle {
transform: Transform::from_xyz(0., 0., 1000.).looking_at(Vec3::ZERO, Vec3::Z),
..Default::default()
});
commands.spawn_bundle(SpriteBundle {
sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
..Default::default()
});
}
pub fn zoom_in(mut query: Query<&mut Transform, With<Camera>>, time: Res<Time>) {
for mut transform in query.iter_mut() {
transform.translation.z -= 100. * time.delta_seconds();
warn!("{}", transform.translation.z);
}
}
You do not see the sprite, because you apparently look at it from the wrong side. If you have a 2D scene I would advise you to stick to the Camera2DBundle.
Contrary to what you stated in your question, in order to zoom you can set the scale of OrthographicProjection like so:
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(zoom_in)
.run();
}
fn setup(
mut commands: Commands
) {
commands.spawn_bundle(Camera2dBundle::default());
commands.spawn_bundle(SpriteBundle {
sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
..Default::default()
});
}
pub fn zoom_in(mut query: Query<&mut OrthographicProjection, With<Camera>>, time: Res<Time>) {
for mut projection in query.iter_mut() {
projection.scale -= 0.1 * time.delta_seconds();
println!("Current zoom scale: {}", projection.scale);
}
}
Note that you might want to implement logarithmic zoom, so that your zoom does "feel" linear and does not speed up approaching infinity when the scale approaches zero.
Here is a sample using logarithmic zoom:
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(zoom_in)
.run();
}
fn setup(
mut commands: Commands
) {
commands.spawn_bundle(Camera2dBundle::default());
commands.spawn_bundle(SpriteBundle {
sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
..Default::default()
});
}
pub fn zoom_in(mut query: Query<&mut OrthographicProjection, With<Camera>>, time: Res<Time>) {
for mut projection in query.iter_mut() {
let mut log_scale = projection.scale.ln();
log_scale -= 0.1 * time.delta_seconds();
projection.scale = log_scale.exp();
println!("Current zoom scale: {}", projection.scale);
}
}

QML How reverse play animation

I want an object, need it to fellow a complex path and move as an animation.
The path is included line and curve. just like a train.
Two solution: 1. PathAnimation or 2. states with multi-animation
Problem of solution 1:
The train maybe stop at a random time-point(pause the animation), and go reverse back to the start position(play animation reversely).
So i want know any way to play PathAnimation reversely?
I think QML doesn't have that functionality.
You could set a new path whenever you need a new one. I.e, when you animation ends.
Here you have an example:
import QtQuick 2.3
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
width: 350
height: 300
Rectangle {
id: window
width: 350
height: 300
Canvas {
id: canvas
anchors.fill: parent
antialiasing: true
onPaint: {
var context = canvas.getContext("2d")
context.clearRect(0, 0, width, height)
context.strokeStyle = "black"
context.path = pathAnim.path
context.stroke()
}
}
SequentialAnimation {
id: mySeqAnim
running: true
PauseAnimation { duration: 1000 }
PathAnimation {
id: pathAnim
duration: 2000
easing.type: Easing.InQuad
target: box
orientation: PathAnimation.RightFirst
anchorPoint: Qt.point(box.width/2, box.height/2)
path: myPath1
}
onStopped: {
console.log("test")
pathAnim.path = myPath2;
mySeqAnim.start();
}
}
Path {
id: myPath1
startX: 50; startY: 100
PathLine { x: 300; y: 100 }
onChanged: canvas.requestPaint()
}
Path {
id: myPath2
startX: 300; startY: 100
PathLine { x: 50; y: 100 }
onChanged: canvas.requestPaint()
}
Rectangle {
id: box
x: 25; y: 75
width: 50; height: 50
border.width: 1
antialiasing: true
Text {
anchors.centerIn: parent
}
}
}
}

How to properly dispose Scene3D from QML?

For my project I need to place QtQuick.Scene3D within QtQuick.Component
Scene is successfully created, but when I try to dispose component I get segfault at
0 Qt3D::QCamera::position() const 0xb7226e6b
1 Qt3D::QCamera::translate(QVector3D const&, Qt3D::QCamera::CameraTranslationOption) 0xb7226fa4
It seems that objects are deleted in wrong order. So, there is a question: should I implement whole object graph in C++, or there is a correct way to make Scene3D re-creatable?
There is my component qml file:
import Qt3D 2.0
import Qt3D.Renderer 2.0
import QtQuick.Scene3D 2.0
import QtQuick 2.0 as QQ2
import CeGui 1.0;
import VectorPlot 1.0;
QQ2.Component {
QQ2.Item {
Scene3D {
anchors.fill: parent
id: rootscene
aspects: "input"
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 25.0, -25.0, 25.0 )
upVector: Qt.vector3d( 0.0, 0.0, 1.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
Configuration {
id: cfg
controlledCamera: camera
}
Viewport {
id: viewport
rect: Qt.rect(0.0, 0.0, 1.0, 1.0) // From Top Left
clearColor: Qt.rgba(0, 0.5, 1, 1)
CameraSelector {
id : cameraSelector
camera: camera
ClearBuffer {
buffers : ClearBuffer.ColorDepthBuffer
}
}
}
components: [
FrameGraph {
id: framgraph
activeFrameGraph: viewport
}
]
BarChartScene {
id: bcs
model: GlobalViewModel.harmonicsEditModel
}
}
}
}
}
I use Qt 5.5 for 32-bit gcc in Linux
It seems that it is known bug, which will be fixed in upcoming releases

Bounds of Flickable.contentY in QML

What is the correct way of asking the bounds of Flickable.contentY? I need that for a scrollbar.
Experimentally I have discovered that
offsetY <= contentY <= offsetY + contentHeight - height
where offsetY can be calculated as
var offsetY = contentY-Math.round(visibleArea.yPosition*contentHeight)
offsetY is zero at the start of the application and seems to be constant unless Flickable is resized.
This formula works in general, but there probably should be a dedicated function for it.
I did a scrollbar easily without offset :
// ScrollBar.qml
import QtQuick 2.0
Rectangle {
id: scrollbar;
color: "#3C3C3C";
visible: (flicker.visibleArea.heightRatio < 1.0);
property Flickable flicker : null;
width: 20;
anchors {
top: flicker.top;
right: flicker.right;
bottom: flicker.bottom;
}
Rectangle {
id: handle;
height: (scrollbar.height * flicker.visibleArea.heightRatio);
color: "#5E5E5E";
border {
width: 1;
color: "white";
}
anchors {
left: parent.left;
right: parent.right;
}
Binding { // Calculate handle's x/y position based on the content position of the Flickable
target: handle;
property: "y";
value: (flicker.visibleArea.yPosition * scrollbar.height);
when: (!dragger.drag.active);
}
Binding { // Calculate Flickable content position based on the handle x/y position
target: flicker;
property: "contentY";
value: (handle.y / scrollbar.height * flicker.contentHeight);
when: (dragger.drag.active);
}
MouseArea {
id: dragger;
anchors.fill: parent;
drag {
target: handle;
minimumX: handle.x;
maximumX: handle.x;
minimumY: 0;
maximumY: (scrollbar.height - handle.height);
axis: Drag.YAxis;
}
}
}
}
Just use it like this :
Flickable {
id: myFlick;
}
ScrollBar {
flicker: myFlick;
}
It can be moved with mouse, and moves automatically when Flickable is scrolled.