How to Use RustEmbed to load Images into an fltk app? - rust

I'm trying to use the RustEmbed crate to embed some svg icons into my executable. I don't see any documentation on how to turn the data returned from Asset::get("icons/some.svg") into an actual image. My GUI library is fltk, and I want to create an fltk::image::SvgImage using Asset but SvgImage::new() loads an svg from a path, not raw byte data. Because it loads the svg from a path and not from raw byte data, does that mean I can't use RustEmbed for embedding the icons into my target build?
I want to do this because I feel like embedding my image assets into the executable will help avoid IO errors when the path of my executable is changed during deployment/install/build. I thought this one of the intentions of the RustEmbed crate.
use rust_embed::RustEmbed;
use fltk::image::*;
#[derive(RustEmbed)]
#[folder = "examples/public/"]
struct Asset;
fn main() {
let svg = Asset::get("icons/eye.svg").unwrap();
println!("{:?}", std::str::from_utf8(svg.as_ref()));
//just prints some long array of numbers [60, 115,118,...60,47,115]
wind_svg = SvgImage::load(svg).unwrap();
}
gives error:
the trait `AsRef<std::path::Path>` is not implemented for `std::option::Option<Cow<'_, [u8]>>`

Turns out that fltk::image::SvgImage has a from_data() function. This can be used create load the svg from byte data:
use rust_embed::RustEmbed;
use fltk::image::*;
#[derive(RustEmbed)]
#[folder = "examples/assets/"]
struct Asset;
fn main() {
let bytes = Asset::get("icons/eye.svg").unwrap();
let svg = SvgImage::from_data(std::str::from_utf8(&bytes).unwrap()).unwrap();
}
For more useful information on dependencies check out this reddit thread.

Related

How to store different Rust GTK Widgets inside an array or a hashmap?

I want to create a dictionary of my gtk widgets in an application. I want to create a HashMap object with (name, widget) pairs. I can't figure out what type should I use for the second parameter in the HashMap:
let mut map: HashMap<&str, ? >=HashMap::new();
map.insert("List", gtk::ComboBoxText::new());
map.insert("Label", gtk::Label::new(Some(""));
I have tried different options like Widget, IsA<Widget>, GObject, WidgetExt, ObjectExt etc but they all won't compile

Frame graph architecture

Since no new shader can be created during runtime, the full set is known ahead at compile-time. Each shader must reference a "pass" in which it will used to render.
To avoid frame-spikes during runtime, I'd like to pre-create all pipeline objects during startup. And to create a pipieline, the number of outputs and the format of each output attachment must be known - either to create a VkRenderPass or to specify the outputs for the dynamic rendering feature.
However, I'd also like to use the frame graph concept (speech by Yuriy O'Donnell) which in turn builds a graph of render passes with input-output specification and dependencies between them. Some passes are conditionally created (e.g. debug passes), some passes might be dropped from the graph (after "compiling" it).
Additionally, I need to support the "write on top" feature, so instead of specifying a new output during the building of the render pass, I can simply say that the output of this pass will use an output from a previous pass - this is useful for adding alpha-blended rendering, for example.
How can I match the two separate section of the code? In other words, how can I define all render passes during initialization but also use a dynamic approach of building the frame graph each frame without repeating myself?
This is what I'd like to avoid (pseudo-code):
struct Pass1Def
{
output1 = ImageFormat::RGBA8;
output2 = ImageFormat::RGBA8;
// ...
outputs = // outputs in order (corresponds to location in shader)
};
void init()
{
for_each_shaders shader {
passDef = findPassDef(shader);
createPipeline(shader, passDef);
}
}
void render()
{
auto previousResource = someCondition ? passA.outputResource1 : passB.outputResource2;
graph.addPass(..., [&](PassBuilder& builder, Pass1Data& data) {
// error-prone: order of function calls matter (corresponds to location in shader)
// error-prone: use the same format defined in Pass1Def
data.outputResource1 = builder.create(... ImageFormat::RGBA8);
// error-prone: the format depends on the outputResource of a previous pass
// however the format must be (and was) specified in Pass1Def
data.outputResource2 = builder.write(previousResource);
});
}

Godot gdnative Rust access property defined in the editor

In order to learn about the engine I'm trying my hand at a very simple project - proceduraly generating a sphere using the editor and the gdnative Rust bindings.
I'm trying to follow up this tutorial which uses GDScript and convert the
code to Rust.
I'm having trouble figuring out how to access a property defined in the editor.
I've been reading the docs and searching around the web for a week now but there is
something that escapes me and I'm not able to arrive at an understanding of how to proceed about it.
What I want to do, is access the mesh property, of type ArrayMesh, much like in the tutorial that I linked above, and attach to it the arrays that I generated for the vertices - basically bind those arrays to the ArrayMesh. Here is my scene:
[gd_scene load_steps=4 format=2]
[ext_resource path="res://procedural_earth.gdnlib" type="GDNativeLibrary" id=1]
[sub_resource type="ArrayMesh" id=4]
[sub_resource type="NativeScript" id=3]
resource_name = "ProcEarth"
class_name = "ProcEarth"
library = ExtResource( 1 )
[node name="Earth" type="Spatial"]
[node name="Sphere" type="MeshInstance" parent="."]
mesh = SubResource( 4 )
script = SubResource( 3 )
[node name="Camera" type="Camera" parent="."]
transform = Transform( 0.572229, -0.327396, 0.751909, 0, 0.916856, 0.399217, -0.820094, -0.228443, 0.524651, 4.71648, 2.5, 3.45846 )
current = true
The ArrayMesh structure that I'm interested in, is called mesh in the above scene and is part of the node named "Sphere"(mentioning just for the sake of clarity).
I have the following Rust code:
#[derive(NativeClass)]
#[inherit(MeshInstance)]
#[register_with(register_properties)]
struct ProcEarth {
// ...
}
impl ProcEarth {
// ...
#[export]
fn _ready(&mut self, owner: &MeshInstance) {
let mut arr = VariantArray::new_shared();
// ...
let blend_shapes = VariantArray::new_shared();
owner.add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, blend_shapes, 0);
}
}
But that does not work as the error I get is:
no method named `add_surface_from_arrays` found for reference `&gdnative::gdnative_bindings::MeshInstance` in the current scope
method not found in `&gdnative::gdnative_bindings::MeshInstance`rustc(E0599)
Does anyone know how could I access in the Rust code that property from the editor, in order to properly set my ArrayMesh? Is there any tutorial, article, video that exemplifies that?
Any pointers highly appreciated as I'm currently stuck into this technicality
and cannot progress my learning.
I'm using Godot version v3.4.stable.official with gdnative 0.9.3 on Linux.
The method add_surface_from_arrays is defined in ArrayMesh. Given the error you got, you are trying to call it on a MeshInstance.
We can confirm that with the source code, since you get owner: &MeshInstance and you are calling owner.add_surface_from_arrays(…).
Usually you would create an ArrayMesh and call add_surface_from_arrays on it passing an array with the vertex data. Afterwards you should be able to call set_mesh on the MeshInstance passing the ArrayMesh.
let mut am = ArrayMesh::new();
let mut arr = VariantArray::new_shared();
// …
let blend_shapes = VariantArray::new_shared();
am.add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, blend_shapes, 0);
owner.set_mesh(am.cast::<Mesh>());
I believe you can call mesh on the MeshInstance to retrieve it. Be aware that it can a Mesh (ArrayMesh or PrimitiveMesh) or nil. The method mesh is documented to return Option<Ref<Mesh, Shared>>.

How to append to existing apache arrow array

I can create an arrow array with a builder:
extern crate arrow;
use arrow::array::Int16Array;
// Create a new builder with a capacity of 100
let mut builder = Int16Array::builder(100);
// Append a slice of primitive values
builder.append_slice(&[2, 3, 4]).unwrap();
// Build the array
let finished_array = builder.finish();
But once I have finished building the array (thus called .finish), is there any option to create a new builder with the data offinished_array without copying the data into a new builder?
What I basically want is a cheap append operation.
After reading some more, I found out arrow arrays are alway immutable. An append operation to an array is not possible. If you want a zero copy append like behavior, you can write/ use a chunked array (this is not yet available in rust, but for instance is supported in pyarrow

How to set property type of qml signal?

I am learning qml,quick and pyqt5 and write a small test script.
In this script, I want to drop something on my UI and print the url of it.
test.qml
import QtQuick 2.3
Rectangle {
id : root
signal clicked(int x, int y)
signal filedroped(list url)
width: 800
height: 450
MouseArea {
anchors.fill: parent
onClicked: {
parent.clicked(mouseX, mouseY)
}
DropArea {
anchors.fill: parent
onDropped: {
root.filedroped(drop.urls)
}
}
}
}
The doc says:Any of the QML Basic Types aside from the enumeration type can be used as custom property types.
But I got error like this in signal filedroped:
Invalid signal parameter type: list
Also, I have tried urllist and string.
urllist failed and string works.
What's wrong with my script?
EDIT
Since I use qml with pyqt, I do not want to use the type var.
With var, I'll got a QJSValue object instead of basic type of python in my python script.
Why qml performs different with the official document? Is the document wrong?
It seems on there's indeed an error in the Qt Documentation. It is said (here) that
the allowed parameter types [for signal parameters] are the same as those listed under
Defining Property Attributes on this page.
Yet one can define a property as follow:
property list<Item> items
whereas this is invalid:
signal mysignal(list<Item> items)
But anyway, the QML list type was not a solution. The official documentation is quite clear:
A list can only store QML objects, and cannot contain any basic type
values. (To store basic types within a list, use the var type
instead.).
In other words you can't use list to store strings, url, int. You have to use var. Another solution would be to use a formatted string with a custom separator instead of your url list, and split it on the Python side.
It looks that urllist is an array of urls so you can use var in this case:
signal filedroped(var urls)

Resources