How to rotate using canvas_item_set_transform? - godot

I'm trying to copy a Sprite node and display it on screen through a VisualServer and so far this has been my progress based on this example
ci_rid = VisualServer.canvas_item_create()
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
self.remove_child(sprite_node);
sprite_node.queue_free();
var texture = load(sprite_node.texture.resource_path);
var pos_and_size=Rect2(Vector2(0,0), sprite_node.region_rect.size);
if(sprite_node.region_enabled==true):
VisualServer.canvas_item_add_texture_rect_region(ci_rid,pos_and_size, texture,sprite_node.region_rect);
var transform_matrix=Transform2D.IDENTITY;
VisualServer.canvas_item_set_transform(ci_rid,transform_matrix.translated(sprite_node.position-sprite_node.region_rect.size / 2))
but when I try to replicate the rotation on it's own axis like this:
VisualServer.canvas_item_set_transform(ci_rid,
transform_matrix.rotated(deg2rad(sprite_node.rotation_degrees)).translated(sprite_node.position-sprite_node.region_rect.size / 2)
)
it doesn't give the desired result and moves the entire image to another position
Original Sprite node:
VisualServer copy:

You can take the global_transform before you remove it:
var sprite_transform := sprite_node.global_transform
Or you can reconstruct it after you removed it:
var sprite_transform = global_transform * Transform2D(sprite_node.rotation, sprite_node.position).scaled(sprite_node.scale)
I'm using global_transform here because the node used to be a child of where the code is running. So its transform is relative to it.
I really don't know how you are not getting errors with your texture. This is the way I ended up going about it:
var image:Image = sprite_node.texture.get_data()
var texture_rid := VisualServer.texture_create_from_image(image)
Then the region and size are handled like this:
var texture_size:Vector2 = image.get_size()
var region:Rect2 = sprite_node.region_rect if sprite_node.region_enabled else Rect2(Vector2.ZERO, texture_size)
var pos_and_size := Rect2(region.size * -0.5 if sprite_node.centered else Vector2.ZERO, region.size)
And these are my VisualServer calls:
var ci_rid := VisualServer.canvas_item_create()
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_add_texture_rect_region(ci_rid, pos_and_size, texture_rid, region);
VisualServer.canvas_item_set_transform(ci_rid, sprite_transform)

Related

VisualServer transform & shape with Physics2DServer body

I'm trying to create a rigid body like Item using only Physics2DServer and VisualServer
like this:
extends Node2D
var _body:RID
var canvasItem:RID
func _enter_tree():
_body=Physics2DServer.body_create();
Physics2DServer.body_set_space(_body, get_world_2d().space);
var shape= RectangleShape2D.new();
shape.extents=Vector2(30,30);
var ci_rid = VisualServer.canvas_item_create() # ci= Canvas Item
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
Physics2DServer.body_add_shape(_body, shape.get_rid(), self.global_transform, false);
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved",ci_rid);
var image=Image.new();
image.load("res://icon.png")
var texture_rid := VisualServer.texture_create_from_image(image)
VisualServer.texture_set_flags (texture_rid,VisualServer.TEXTURE_FLAG_ANISOTROPIC_FILTER)
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(image.get_size() * -0.5, image.get_size()), texture_rid);
VisualServer.canvas_item_set_transform(ci_rid, self.transform)
func _body_moved(state:Physics2DDirectBodyState,ci_rid):
VisualServer.canvas_item_set_transform(ci_rid,state.transform)
but for some reason the collision is not working
Edit:
I think the main problem is the var shape= RectangleShape2D.new();
because when I added a export(Shape2D) var shape; instead and added a RectangleShape2D manually, then the collision worked properly
Edit for transform based problem:
extends Node2D
var _body:RID
var _shape:RID
var canvasItem:RID
func _enter_tree():
_body=Physics2DServer.body_create();
Physics2DServer.body_set_space(_body, get_world_2d().space);
var ci_rid = VisualServer.canvas_item_create() # ci= Canvas Item
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
_shape = Physics2DServer.rectangle_shape_create()
Physics2DServer.shape_set_data(_shape, Vector2(30,30))
Physics2DServer.body_add_shape(_body,_shape);
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved",ci_rid);
var texture:Texture = load("res://icon.png")
var image:Image = texture.get_data()
var texture_rid := VisualServer.texture_create_from_image(image)
VisualServer.texture_set_flags(texture_rid,VisualServer.TEXTURE_FLAG_ANISOTROPIC_FILTER)
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(image.get_size() * -0.5, image.get_size()), texture_rid);
VisualServer.canvas_item_set_transform(ci_rid, Transform2D.IDENTITY)
set_notify_transform(true)
func _exit_tree():
if(_body.get_id()!=0):
Physics2DServer.free_rid(_body)
if(_shape.get_id()!=0):
Physics2DServer.free_rid(_shape)
func _body_moved(state:Physics2DDirectBodyState,ci_rid):
VisualServer.canvas_item_set_transform(ci_rid,state.transform)
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSFORM_CHANGED:
if _body.get_id() != 0:
Physics2DServer.body_set_state(_body, Physics2DServer.BODY_STATE_TRANSFORM, transform)
Missing Shape
The problem with the shape is that it is getting freed.
If you declare a field with the shape at the top of the script, and then initialize it _enter_tree, it works correctly. But declared inside _enter_tree it lives until the end of the method when it goes out of scope.
The lesson is that a RID is not a reference.
The alternative is to create the shape and the texture with the Physics2DServer:
var shape_rid := Physics2DServer.rectangle_shape_create()
Physics2DServer.shape_set_data(shape_rid, Vector2(30,30))
And since that is a RID created with the Physics2DServer you would free it by calling the free_rid method of the Physics2DServer.
By the way, I'm getting a warning about the texture. You can load it as a Texture instead:
var texture:Texture = load("res://icon.png")
var image:Image = texture.get_data()
Which will the warning go away.
And then we are tempted to do this:
var texture_rid := texture.get_rid()
But we get errors… The issue is the same, the texture is getting freed when the method ends because the variable is the only reference and it is going out of scope. Declaring the texture at the top of the file fixes the error spam.
And, yes, we could create the texture with the VisualServer. But since you are loading it from a resource, it is not practical.
Transform
First of all, the canvas item is positioned relative to the node where the code is running:
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_set_transform(ci_rid, Transform2D.IDENTITY)
Second, the body is placed in global coordinates. So we should do this:
Physics2DServer.body_set_state(_body, Physics2DServer.BODY_STATE_TRANSFORM, global_transform)
Notice global_transform instead of transform.
And that brings us to this:
state.transform is global coordinates.
canvas_item_set_transform wants coordinates relative to the parent (the node where the code is running).
This a way to deal with it:
VisualServer.canvas_item_set_transform(ci_rid, global_transform.inverse() * state.transform)
Alternatively, you could move the node to follow the body:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
global_transform = state.transform
And that would move the canvas item too, because, remember that it is parented (with canvas_item_set_parent).
Since you are moving the body when the node moves (in _notification), I believe moving the node to follow the body is the correct solution. This way, they stay in sync all the time.
About the shape: it is placed relative to the body. Both the shape and the body can move. The physics engine will move the body (and the shape follows because it is positioned relative to the body), but the physics engine will not move the shape directly. You, however, could move the shape directly if you wanted. This is the same relationship that a RigidBody2D and its CollisionShape2D would have.

Drag & Drop (swap) 3 texture buttons in a Margin Container->VBox

Following along a great tutorial, code is replicated yet does not function as shown. No new texture appears to be created when I try to drag the texture button. Only difference is the tutorial uses a TextureRect, while I'm using a TextureButton.
extends TextureButton
func get_drag_data(position: Vector2):
var vControl = Control.new()
var vData = {}
var vTexture = TextureRect.new()
vTexture.expand = true
vTexture.texture = texture_disabled
vTexture.rect_size = Vector2(320, 400)
vControl.add_child(vTexture)
vTexture.rect_position = -0.5 * vTexture.rect_size
set_drag_preview(vTexture)
return vData
Code above is attached to Party_1. texture_disabled does have a texture set.
It would work if you had the code in get_drag_data looking like this:
func get_drag_data(_position: Vector2):
var vTexture = TextureRect.new()
vTexture.expand = true
vTexture.texture = texture_disabled
vTexture.rect_size = Vector2(320, 400)
set_drag_preview(vTexture)
return {}
However, that would place the TextureRect top-left corner at the cursor position.
You want to offset the position of the TextureRect so it is centered at the cursor position. To do that, you create another Control, add the TextureRect as a child to it… And pass the TextureRect anyway? No, that does not work, you need to pass the new Control:
func get_drag_data(_position: Vector2):
var vTexture = TextureRect.new()
vTexture.expand = true
vTexture.texture = texture_disabled
vTexture.rect_size = Vector2(320, 400)
var vControl = Control.new()
vControl.add_child(vTexture)
vTexture.rect_position = -0.5 * vTexture.rect_size
set_drag_preview(vControl)
return {}
Please notice I'm giving vControl to set_drag_preview, not vTexture.
You cannot give to set_drag_preview a Control that has a parent. In fact, if you tried you would get an error in the Output console saying:
_gui_set_drag_preview: Condition "p_control->get_parent() != nullptr" is true.
Or
_gui_set_drag_preview: Condition "p_control->is_inside_tree()" is true.

svg.js rotating element rotates whole coordinate system

Using svg.js version 2.1.1
When trying to rotate object and then move it, it looks like the whole coordinate system is rotating, and not only the object. It's like both axes are rotating with the object. This is not desired by me - I would like to use absolute coordinates, not relative to object.
Maybe I don't understand SVG or svg.js and I do something wrong, but it looks ridiculous to me.
Sample code
var draw = SVG('drawing').size(300,300)
var img = 'https://pbs.twimg.com/profile_images/378800000674268962/06ce58cab26c3a0daf80cf57e5acb29b_400x400.jpeg'
var picture = draw.image(img, 100, 100)
picture.rotate(30)
var i = 20
picture.click(function() {
this.move(i,0)
i += 20
})
Here's the fiddle example of what I'm talking about: https://jsfiddle.net/dudek3370/swzqo5ye/8/
If you want to work with the non-rotated co-ordinate system and rotate something then simply place the rotated item in a container that you don't rotate and then move the container. E.g.
var draw = SVG('drawing').size(300,300)
var img = 'https://pbs.twimg.com/profile_images/378800000674268962/06ce58cab26c3a0daf80cf57e5acb29b_400x400.jpeg'
var group = draw.group();
var picture = group.image(img, 100, 100)
picture.rotate(30)
var i = 20
group.click(function() {
this.move(i,0)
i += 20
})
<div id="drawing"></div>
<script src="https://s3-eu-west-1.amazonaws.com/svgjs/svg.js" type="text/javascript" charset="utf-8"></script>

How to zoom all elements pof canvas with one click?

I m using fabric.js in one of my projects.
I m using zooming functionalities . I would like to know if there a simple way to zoom all the canvas (with all the elements ) in on click.
Try out this jsfiddle demonstrating zoom onclick of a button: http://jsfiddle.net/cmontgomery/H7Utw/1/. In essence you get all objects on the canvas and scale/move them by the same factor, giving the visual appearance of zoom.
var objects = canvas.getObjects();
for (var i in objects) {
var scaleX = objects[i].scaleX;
var scaleY = objects[i].scaleY;
var left = objects[i].left;
var top = objects[i].top;
var tempScaleX = scaleX * SCALE_FACTOR;
var tempScaleY = scaleY * SCALE_FACTOR;
var tempLeft = left * SCALE_FACTOR;
var tempTop = top * SCALE_FACTOR;
objects[i].scaleX = tempScaleX;
objects[i].scaleY = tempScaleY;
objects[i].left = tempLeft;
objects[i].top = tempTop;
objects[i].setCoords();
}
canvas.renderAll();
If you use zoom with fabricjs without modifying all objects use: https://github.com/wojtek-krysiak/fabricjs-viewport.
Example: http://wojtek-krysiak.github.io/fabricjs-viewport/

Best method of scaling text to fill an HTML5 canvas

I need to 'scale' text to fill an antire HTML5 canvas. It appears that the scale() method does not affect text. I've seen approximation methods with iterative loops on the measureText() method but this doesn't get me exactly what I need. Ideally I'd like to fill both horizontally and vertically without conserving the aspect ratio. Would SVG possibly be able to help with this?
My bad - Scale DOES apply to text. I've come up with a solution:
context.font = "20px 'Segoe UI'";
var metrics = context.measureText("Testing!");
var textWidth = metrics.width;
var scalex = (canvas.width / textWidth);
var scaley = (canvas.height / 23);
var ypos = (canvas.height / (scaley * 1.25));
context.scale(scalex, scaley);
context.fillText("Testing!", 0, ypos);
Scale does affect text. Try this:
var can = document.getElementById('test');
var ctx = can.getContext('2d');
ctx.fillText("test", 10, 10); // not scaled text
ctx.scale(3,3);
ctx.fillText("test", 10, 10); // scaled text
See it in action here:
http://jsfiddle.net/3zeBk/8/

Resources