How can I "best fit" an arbitrary cairo (pycairo) path? - graphics

It seems like given the information in stroke_extents() and the translate(x, y) and scale(x, y) functions, I should be able to take any arbitrary cairo (I'm using pycairo) path and "best fit" it. In other words, center it and expand it to fill the available space.
Before drawing the path, I have scaled the canvas such that the origin is the lower left corner, up is y+, right is x+, and the height and width are both 1. Given these conditions, this code seems to correctly scale the path:
# cr is the canvas
extents = cr.stroke_extents()
x_size = abs(extents[0]) + abs(extents[2])
y_size = abs(extents[1]) + abs(extents[3])
cr.scale(1.0 / x_size, 1.0 / y_size)
I cannot for the life of me figure out the translating though. Is there a simpler approach? How can I "best fit" a cairo path on its canvas?
Please ask for clarification if anything is unclear in this question.

I have found a solution that I like (at least for my purposes). Just create a new surface and paint the old surface on to the new one.

As for the scale only, I have done a similar thing to adjust an image inside a box with a "best-fit" approach. As about scale, here is the code:
available_width = 800
available_height = 600
path_width = 500
figure_height = 700
# formulas
width_ratio = float(available_width)/path_width
height_ratio = float(available_height)/figure_height
scale = min(height_ratio, width_ratio)
# result
new_path_width = path_width*scale
new_figure_height = figure_height*scale
print new_path_width, new_figure_height
The image gets drawn aligned to the origin (top left in my case), so perhaps a similar thing should be done to translate the path.
Also, this best fit is intended to preserve aspect ratio. If you want to stretch the figure, use each of the ratios instead of the 'scale' variable.
Hope I have helped

Related

Why isn't my path2d position updating?

I created a new path2d following the instructions in the my first game article: http://docs.godotengine.org/en/3.0/getting_started/step_by_step/your_first_game.html
I wanted to move the "box" on screen so that I could see how the mobs spawn in, but when I ran the scene, it stayed off screen.
I created a new path2d, centered this one in the middle of the screen, and it works like I wanted it to, but now I moving this one in the editor doesn't update the position in game.
What's going on?
Thanks
func _on_mobtimer_timeout():
$mobtimer.wait_time = 0.1 + randf() / 2
$mobspawn/moblocation.set_offset(randi())
var mob = Mob.instance()
add_child(mob)
var direction = $mobspawn/moblocation.rotation + PI/2
mob.position = $mobspawn/moblocation.position
direction += rand_range(-PI/8, PI/8)
mob.rotation = direction
mob.set_linear_velocity(Vector2(rand_range(200, 200 + score * 30), 0).rotated(direction))
A Node2D's position property is relative to it's parent's position. The code from the Dodge The Creeps tutorial assumes that MobPath is located at 0, 0 and fails when that assumption is false.
In your case you are taking a MobSpawnLocation's position relative to MobPath and then setting it as the new Mob's global position.
Luckily Node2D's have another property that we can use in these circumstances global_position. It can be used like this:
mob.position = $mobspawn/moblocation.global_position
http://docs.godotengine.org/en/stable/classes/class_node2d.html#member-variables
This isn't a full solution, but I found a weird workaround. Instead of changing the position in the editor, if you use the nodes on the orange box (at the intersection of orange and blue), you can kind of alternate to move the box around.

OpenCV Seamless Cloning shift position after finish the process

I am trying to used the seamsless cloning to blend to image together.
but I notice that after using the seamsless clone function the area in the
mask that I want to transfer is shift upward. So I have a question that
is this a normal behaviour of the seamsless clone function or it is a bug
on my implementation.
Here are the Source photo
Here are the destination photo
Here are the result photo
I encountered similar situation. Moreover, like #JoshuaCWebDeveloper noted, this shift disappeared when all one mask is used. Nevertheless, I got a fix for this. What I did is this. I cropped valid mask (non-zero sub-section) out using cv2.boundingRect. So my source image and mask image are reduced to a smaller size, while center is now calculated from boundingRect outputs (Since reference point is marked on destination image). This way, error got solved/shift got ridden.
(Based on the answer posted by Fractalic Forieu) You can achieve the same result without reducing the image size.
Instead of using the image center:
center = (width // 2, height // 2)
poissonImage = cv2.seamlessClone(srcImage, dstImage, maskImage, center)
use the center of the bounding rect:
monoMaskImage = cv2.split(maskImage)[0] # reducing the mask to a monochrome
br = cv2.boundingRect(monoMaskImage) # bounding rect (x,y,width,height)
centerOfBR = (br[0] + br[2] // 2, br[1] + br[3] // 2)
poissonImage = cv2.seamlessClone(srcImage, dstImage, maskImage, centerOfBR )

d3.js geo - rendering svg path

I'd like to create choropleth map of Czech Republic. Inspired by this article http://bl.ocks.org/mbostock/4060606, I have created this
http://jsfiddle.net/1duds8tz/2/
var width = 960;
var height = 500;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var offset = [width / 2, height / 2];
var projection = d3.geo.mercator().scale(6000).center([15.474, 49.822]).translate(offset);
var path = d3.geo.path().projection(projection);
queue().defer(d3.json, "..map.geojson").await(ready);
function ready(error, reg) {
var group = svg.selectAll("g").data(reg.features).enter().append("g");
group.append("path").attr("d", path).attr("fill", "none").attr("stroke", "#222");
}
When I tried to fill svg path with some color, I ended on this
http://jsfiddle.net/1duds8tz/3/
group.append("path").attr("d", path).attr("fill", "red").attr("stroke", "#222");
There are odd values in path d attribute.
My GeoJSON data must be somehow faulty but I can't figure what is wrong.
Everything looks right here: https://gist.github.com/anonymous/4e51227dd83be8c2311d
Your geoJSON is corrupted and as a result your polygons are being drawn as the interiors of an infinitely bounded polygon. That's why when you attempt to give a fill to the path, it goes beyond the extent of the screen but still displays the border just fine. I tried to reverse the winding order of your coordinates array, and that seemed to fix all of them except for "Brno-venkov", which might be the source of your problems (especially given its administrative shape).
I'd suggest going back to where you created the original GeoJSON and try to re-export it with simplification. If you want to reverse the coordinates on your GeoJSON to correct the winding order, that's pretty simple:
geodata = d3.selectAll("path").data();
for (x in geodata) {geodata[x].geometry.coordinates[0] = geodata[x].geometry.coordinates[0].reverse()}
But this won't fix the problem polygon, nor will not reversing its coordinates.
In case you are familiar with svg manipulation you can try geojson2svg. This allows you manipulate svg in standard way but you have to code a little more. In case your application requires d3 for many other purpose then d3 is best solution.
I've got exactly the same problem with Mapzen's .geojson files.
.reverse()-ing isn't good enough, if you can't make sure all your data has the same winding order.
I solved it with this one:
https://www.npmjs.com/package/geojson-rewind
You'll need to have npm & require available
Install it, and save it to your project
npm i -g geojson-rewind
Import it, to make it useable
var rewind = require('geojson-rewind');
Use it on the data, in this case:
req = rewind(req);
Tip: If you are working with static data, you can do this only once on the console, and you're good to go.

Efficient way of scaling hundreds of QGraphicItems

I have a zoomable QGraphicsView with scene that contains hundreds (and even thousands) of datapoints. The points are represented by QGraphicsEllipseItems and collected into a QGraphicsItemGroup. When the view is zoomed in to I want datapoints to stay at a constant size (i.e., the distances between neigbouring points increase but the sizes stay the same). Right now I achieve this by running this code every time the user zooms in:
#get all the QGraphicsEllipseItems that make up the QGraphicsItemGroup
children = graphics_item_group.childItems()
for c in children:
#base_size_x and base_size_y are the sizes of the
#untrasformed ellipse (on the scene) when zoom factor is 1
#New width and height are obtained from the original sizes and
#the new zoom factors (h_scale, v_scale)
new_width = base_size_x/h_scale
new_height = base_size_y/v_scale
#The top-left corner of the new rectangle for the item has to be recalculated
#when scaling in order to keep the center at a constant position
#For this, the center of the item has to be stored first
old_center_x = c.rect().center().x()
old_center_y = c.rect().center().y()
#New coordinates of the rectangle top left point are calculated
new_topleft_x = old_center_x - new_width/2.
new_topleft_y = old_center_y - new_height/2.
#Finally a new rectangle is set for the ellipse
c.setRect(new_topleft_x, new_topleft_y, new_width, new_height)
This code works. The problem is that it is quite slow (without the compensatory scaling zooming in/out works very smoothly). I tried turning off antialiasing for the view but it makes things look pretty ugly. Is there anything else that I can do to make the processing/redrawing faster?
In the constructor of the QGraphicsItem:
setFlag(ItemIgnoresTransformations);
When you zoom, the item will remain the same size, and you won't have to manually scale it.

Raphael - how to draw a path that stretches to width?

This may be a very dumb question, but how do I draw a path that will stretch to a div's width?
I am experimenting with Raphaeljs to make an interactive chart: the user can click sparklines and the lines shift up and down to reveal text content. I see that Raphael's rectangles and other shapes will stretch to width fine by setting the width to 100%, but I can't get a line to do this.
I've set up the paper like this:
var SLAPaper = new Raphael( document.getElementById("LineSLA"), "100%", 60);
Set up the line like this:
var lineSLA = SLAPaper.path("F1 M 0,42L 103,12L 222,45L 390,13L 460,45L 453,27L 455,28L 450,0L 479,25");
I also set a viewbox but this doesn't seem to make a difference. I can't set a % width on the viewbox anyway:
SLAPaper.setViewBox(0,0,1500,60, true);
Any help appreciated. Thanks.
I've personally had problems with using percentages for defining size of both paper and objects in Raphael, especially with Internet Explorer. The best I could come up with would be to scale the line to fit inside paper, based on the width of the path (assuming that the width is more than the height):
var line = SLAPaper.path("F1 M 0,42L 103,12L 222,45L 390,13L 460,45L 453,27L 455,28L 450,0L 479,25");
var scl = SLAPaper.width / line.getBBox().width;
line.transform('S' + scl + ',' + scl + ',0,0');

Resources