How to get group element render position? - svg

Refer to the code below, the position of the bounding box is not the actual render position of the group of the elements.
The group element can be used to construct a very complicated unit like a Tank / Ship with cannons.
And group element doesn't have X or Y property to help move the inner elements so I have to use the transform to move the Tank / Ship.
However, I realized that when I translate the group, the bounding box never reflect the real render position any more, does any one have any idea how to get the actual render position of the group?
http://jsfiddle.net/k4uhwLj4/
var root = document.createElementNS("http://www.w3.org/2000/svg", "svg");
root.style.width = '500px';
root.style.height = '500px';
document.body.appendChild(root);
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.setAttributeNS(null, 'transform', 'translate(50, 50)');
root.appendChild(g);
var r = document.createElementNS("http://www.w3.org/2000/svg", "rect");
r.setAttribute("x", "50");
r.setAttribute("y", "60");
r.setAttribute("width", "100");
r.setAttribute("height", "110");
r.setAttribute("fill", "blue");
r.setAttributeNS(null, 'transform', 'translate(50, 50)');
g.appendChild(r);
var c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
c.setAttribute("cx", "150");
c.setAttribute("cy", "140");
c.setAttribute("r", "60");
c.setAttribute("fill", "red");
g.appendChild(c);
var bbox = g.getBBox();
var o = document.createElementNS("http://www.w3.org/2000/svg", "rect");
o.setAttribute("x", bbox.x);
o.setAttribute("y", bbox.y);
o.setAttribute("width", bbox.width);
o.setAttribute("height", bbox.height);
o.setAttribute("stroke", 'black')
o.setAttribute("fill", 'none');
root.appendChild(o);

The .getBBox() method doesn't take transformation into account (per spec) according to this post:
How is the getBBox() SVGRect calculated?
To solve this you can add a parent g that has no transform attribute:
var parentG = document.createElementNS("http://www.w3.org/2000/svg", "g");
root.appendChild(parentG);
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.setAttributeNS(null, 'transform', 'translate(50, 50)');
parentG.appendChild(g);
http://jsfiddle.net/sydgc091/

Related

How to rotate using canvas_item_set_transform?

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)

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.

How do I get the shadow color value of some text in Fabric JS?

I can't seem to find any reference in the Fabric.js docs (or anywhere else) on how to get the shadow color value of some text placed on the canvas?
I'm quite new to Fabric.js.
Here's how to get the rgb values of a text shadow color:
...
var activeObject = canvas.getActiveObject();
var shadColor = activeObject.shadow.color;
var shadProps = shadColor.split(",");
var r = shadProps[0];
r = r.replace("rgb(", "");
var g = shadProps[1];
var b = shadProps[2];
b = b.replace(")", "");
console.log('Red: '+r+' Green: '+g+' Blue: '+b);
...
if you have a reference to the fabric object it's located at: fabricObject.shadow.color

Nesting data using D3.js (heatmap)

So I am new to working with Javascript (especially the D3 library) and I am trying to do something not unlike the following example: http://mbostock.github.com/d3/talk/20111116/iris-splom.html. In my case though each cell is the same thing a 4 x 4 grid with exactly the same scale.
So in my case the top level element is a plate. Each plate has rows and columns at the intersection of a row and column a value (a heatmap if you will). I able to create the proper plate elements; however, the data for ALL plates is present under each element rather than properly nested. I tried to attach an image so you can see that each "plate" is the same, if you look at the underlying document structure it is the same and essentially each rectangle is two overlaid data points.
In looking more closely at Mike's example (link above), it looks like he uses a cross function to help out with the nesting of data, I am wondering if that is where my code falls down. Thank you for any help you all can provide.
I have posted my code below
d3.csv("plateData.csv", function(data) {
var m = 20,
w = 400,
h = 300,
x_extent = d3.extent(data, function(d){return d.Rows}),
y_extent = d3.extent(data, function(d){return d.Columns}),
z_extent = d3.extent(data, function(d){return d.Values});
var x_scale = d3.scale.linear()
.range([m, w-m])
.domain(x_extent)
var y_scale = d3.scale.linear()
.range([h-m,m])
.domain(y_extent)
var ramp=d3.scale.linear()
.domain(z_extent)
.range(["blue","red"]);
// Nest data by Plates
var plates = d3.nest()
.key(function(d) { return d.Plate; })
.entries(data);
// Insert an svg element (with margin) for each plate in our dataset.
var svg = d3.select("body").selectAll("svg")
.data(plates)
.enter().append("svg:svg")
.attr("width", w)
.attr("height", h)
//add grouping and rect
.append("svg:g")
.selectAll('rect')
.data(data)
.enter()
.append('svg:rect')
.attr('x', function(d){return x_scale(d.Rows)})
.attr('y', function(d){return y_scale(d.Columns)})
.attr('width', 10)
.attr('height', 10)
.style('fill', function(d){return ramp(d.Values)});
}
);
AND example Data:
Plate,Rows,Columns,Values
12345,1,1,1158.755
12345,1,2,1097.768
12345,1,3,1097.768
12345,1,4,914.807
12345,2,1,1189.249
12345,2,2,1128.261
12345,2,3,1433.197
12345,2,4,701.352
12345,3,1,914.807
12345,3,2,1433.197
12345,3,3,1189.249
12345,3,4,1402.703
12345,4,1,1158.755
12345,4,2,1067.274
12345,4,3,701.352
12345,4,4,1372.21
56987,1,1,20.755
56987,1,2,97.768
56987,1,3,97.768
56987,1,4,14.807
56987,2,1,89.249
56987,2,2,28.261
56987,2,3,33.197
56987,2,4,15.352
56987,3,1,2000.807
56987,3,2,14.197
56987,3,3,89.249
56987,3,4,402.703
56987,4,1,158.755
56987,4,2,3067.274
56987,4,3,701.352
56987,4,4,182.21
You problem has two sources:
You are inserting one svg element for each nested group, but you don't have defined a position for each element (all the elements are in the body).
You are selecting all the svg elements in the body, and appending one rectangle for each data element, without considering the nested structure.
One solution is to create one svg element inside the body, one group for each plate, define a position for each plate, translate the group to its position (in the svg element) and then create the rectangles for the heatmap.

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/

Resources