how to calculate the easing for counter scaling nested elements - javascript

I want to scale an element with a child element and make it look like the child is not affected by the scaling.
The counter scaling can be done by 1/parentScale, which is fine for the start and end values but not for everything else. The combined increasing and decreasing of the child element is not always seamless.
I am seeking a way to calculate the cubic-bezier points of the child element based on the easing and scaling of the parent, as well as the scaling of the child like below:
parentScale = 0.2
parentEasing = cubic-bezier(0.25, 0.1, 0.25, 1) //"ease"
childScale = 1/0.2 = 5
parentEasing = ?
Here is a pen showcasing the issue: https://codepen.io/nirazul/pen/oLoQMM => on hover the parent gets scaled down to 0.5, and the child gets scaled up to 2
I found an older thread addressing the topic and also giving some explanation as to why this happens but in there only results are given without much detail / steps. The accepted answer literally says that they don't want to explain how they got the solution. The second answer is more detailed but I still don't get how they got from combining scale operations to the actual easing values.

Related

2D overlapping rectangles occlusion

I'm looking for an algorithm that can find intersecting rectangles that overlap each other.
The catch is that my data structure is similar to a quadtree with bounding boxes instead of points.
I'm doing a basic rectangle intersection check but the problem is as I zoom into the tree the child nodes get detected and the parents, I would like to exclude the parent if its fully occluded by a child for the given camera rectangle.
zoom animation
As you can see from the image above as the camera rectangle (black box) sits inside the green node the purple node is still highlighted (filled), consequently as I zoom more and more the parents are always highlighted, even though the camera rectangle can be fully filled with child nodes only.
This makes sense since the camera rectangle is still inside the parent but I have searched and thought about the problem for a while and can't seem to figure out an elegant solution. There seems to be several ways of doing this for 3D spaces but I can't find anything simple for 2D AABB rectangles.
A few solutions that I thought of:
Subtract the child nodes from the parents resulting in concave polygons and then perform polygon intersection.
Use the fill color to check which rectangles are visible, therefore occluding the ones behind.
Perform raycasting or sub-division and check which is the smallest node for a given section.
Is there a better way to do this?
Thank you
Update 1
I have solved the problem by subdividing the camera into smaller sections and for each section the smallest intersecting node is found. This works but there must be a more efficient and cleaner way of doing this.
Update 2
Thank you Trentium for your answer. I can clearly see that an algorithm like that would be a lot more performant than what I'm currently doing.
Eventually I will implement it as splitting a rectangle into smaller rectangles and not polygons sounds like a fun challenge.
Also, I did some very non scientific benchmarks on the current approach and it takes 0.5ms-1ms to both filter and draw everything, so for now performance is still not a concern.
Suggest considering a variation of your first solution proposed "Subtract the child nodes from the parents resulting in concave polygons and then perform polygon intersection."
Specifically, if the immediate children are wholly contained within the parent, then suggest that for each rectangle, that an associated array of visible residual rectangles also be maintained. And then use this array of residual rectangles as the means of determining if the camera / viewport rectangle includes the parent or not.
For example, let's say the parent rectangle is (0,0) - (100,100) and there is an initial child rectangle added at (75,75)-(100,100). The data structure will appear as...
parent.rectangle = {0,0,100,100}
parent.visible = [ {0,0,100,75}, {0,75,75,100} ]
child1.rectangle = {75,75,100,100}
child1.visible = [ {75,75,100,100} ]
...and then if a second child comes along, say at {50,50,75,90}, then this new child is checked against each residual rectangle in the parent.visible array, subdividing the parent visible rectangles further as necessary...
parent.rectangle = {0,0,100,100}
parent.visible = [ {0,0,100,50}, {0,50,50,75}, {75,50,100,75}, {0,75,50,100}, {50,90,75,100} ]
child1.rectangle = {75,75,100,100}
child1.visible = [ {75,75,100,100} ]
child2.rectangle = {50,50,75,90}
child2.visible = [ {50,50,75,90} ]
This method will add a bit of work up front adjusting the immediate parent's visible rectangles as children are added, but should greatly reduce the amount of rectangle intersection tests relative to the current algorithm that involves subdivision of the camera / viewport. Plus this proposed algorithm only makes use of rectangle-to-rectangle intersection tests (both when adding a child to a parent, and when testing the camera / viewport intersections!) rather than the suggested rectangle-to-polygon tests...

Zoomable timeline with one element per tick (D3V6)

I'm working on a timeline-based chart that should always have one rect per x-axis tick. The idea is to use the color of each rectangle to convey the amount of data that's in that date (somewhat like a heatmap).
The chart also has grouped y values. That is, there are two y axes: one for each group, and inside each of those, three lines (subgroups). Semantically the intention is to say: "For this coupon, on this date, there are this many readings for each data type". To help clear up my intention further, this library has a very similar solution to what I'm aiming at.
OK, and your question...?
My question is about aligning the rectangles with the ticks, and keeping them that way. How can I make them always be aligned with the ticks? Is there a way to "tie" a rect to a tick?
To make matters worst, the chart has to be zoomable, so when you scroll and the dimensions of the ticks change, the rectangles should stick to their corresponding ticks: move with them, change their width accordingly and so on. Basically, I'd like to "marry" the rects to their corresponding ticks.
What have you tried?
My approach right now is to make the segments' width depend on the current tick count, and essentially treat the current ticks as if they were my data.
That is:
segmentWidth = state.width() / ticks.length;
And when updating/rendering the rects:
state.mainG.selectAll('g.couponData')
.selectAll('g.column')
.data(ticks)
...
One problem I see with this is that if you click and drag, the ticks expand, so having a single, global segmentWidth doesn't seem like the right approach. Seems like I'd need a way to grab each tick and get the distance to the next one, and use that as the width of each corresponding segment, but I haven't quite got there yet.
Here's a fiddle with an example of what I have:
https://jsfiddle.net/bnekvp0o/11/
TBH, I presume I'm making some dumb calculation mistakes, but I also feel like I'm bruteforcing the solution, so I'm hesitant to keep trying to make it work with my current approach, since I'm quite new to the framework.
Thanks in advance!

How to find one image inside of another? Node.js

I have 2 bmp images. ImageA is a screenshot (example) ImageB is a subset of that. Say for example, an icon.
I want to find the X,Y coordinates of ImageB within ImageA (if it exists).
Any idea how I would do that?
This is called optical-recognition. It may seem complicated (it is) but can be very simple in implementation, so don't shy away from it!
Let Image A be the image we're looking for, and Image B be the larger image with Image A in it.
Method 1
If Image A's scale in Image B hasn't been altered, and the colors are all preserved, you can place Image B on an HTML 5 canvas and iterate over the pixel data. You would load the first line of pixels from Image A and then iterate over every pixel in Image B. If a pixel was the same, you would store that pixels column in a variable and check if the next matched too. If the first row was a full match, then hop to the next row and compare those. You'd repeat that until you either got a match or hit an (or enough) pixels that didn't match. In that case, you would reset all variables and start all over again looking for a match to row 1.
Method 2
If Image A isn't perfectly identical in Image B, new complications arise and things become a lot more complicated. If only the scale changes, we can make a few tweaks to Method 1 to get something that works. Instead of grabbing any pixel and seeing if 80% or so matches, we additionally need to track the images sheer/compression.
In each row, go over pixel incrementally. For example, we'll check every tenth pixel. If we find a match for pixel 1, we then check 10 pixels away and see if that pixel exists anywhere in our row. If we find it, the distance from 0 to that pixel divided by 10 (our increment) is how many times larger the original image is.
If we found a pixel 20 slots from 0 in Image A, and it was only 10 pixels apart in Image B (remember, 10 is our increment), then our original image was 2 times larger. In other words, the new image is half the size of the original.
1) compression = target_width / original_width
2) compression = 20 / 10
3) compression = 2
This is a much more complex but robust way to detect a match. Enough matching rows mean you've got a matching image, but what about vertical stretching?
Similar logic. If you find a row that matches, start at 0 and go down by 10, then find that pixel's match in Image A.
Edit
The methods I provided are generic methods to work with looking for any image inside any other image. As you can imagine this is performance intensive. I don't know what image you're trying to detect but if there are common shapes, sometimes you can do alternative algorithms. If you have a circle, for example, you can just check that there are pixels that match outside a radius and pixels that are the same within.
The methods I presented also don't compensate for warping. Method 2 should be fine if the image is stretched but keeps a rectangular ratio. If the image has for example been warped into a circle shape, things get infinitely more complicated. For that case, the only hint I could give would be to check pixels within a radius of the original for matches.

Inner div inside an outer rotated div does not follow mouse in case dragging with jQuery UI

I have an inner div inside an outer div. The inner div is draggable and outer is rotated through 40 degree. This is a test case. In an actual case it could be any angle. There is another div called point which is positioned as shown in the figure. ( I am from a flash background . In Flash if I were to drag the inner div it would follow the mouse even if its contained inside an outer rotated div.) But in HTML the inner div does not follow the mouse as it can be seen from the fiddle. I want the div 'point' to exactly follow the mouse. Is this possible. I tried to work it using trignometry but could not get it to work.
http://jsfiddle.net/bobbyfrancisjoseph/kB4ra/8/
Here is my approach to this problem.
http://jsfiddle.net/2X9sT/21/
I put the point outside the rotated div. That way I'm assured that the drag event will produce a normal behavior (no jumping in weird directions). I use the draggable handler to attach the point to the mouse cursor.
In the drag event, I transform the drag offset to reflect the new values. This is done by rotating the offset around the outer div center in the opposite direction of the rotation angle.
I tested it and it seems to be working in IE9, Firefox, and Chrome.
You can try different values for angle and it should work fine.
I also modified the HTML so it is possible to apply the same logic to multiple divs in the page.
Edit:
I updated the script to account for containment behavior as well as cascading rotations as suggested in the comments.
I'm also expirementing with making the outer div draggable inside another div. Right now it is almost working. I just need to be able to update the center of the dragged div to fix the dragging behavior.
Try Dragging the red div.
http://jsfiddle.net/mohdali/kETcE/39/
I am at work now, so I can't do the job for you, but I can explain the mathematics behind the neatest way of solving your problem (likely not the easiest solution, but unlike some of the other hacks it's a lot more flexible once you get it implemented).
First of all you must realize that the rotation plugin you are using is applying a transformation to your element (transform: rotate(30deg)), which in turn is changed into a matrix by your browser (matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)).
Secondly it is necessary to understand that by rotating an element the axis of the child elements are rotate absolutely and entirely with it (after looking for a long time there isn't any real trick to bypass this, which makes sense), thus the only way would be to take the child out of the parent as some of the other answers suggest, but I am assuming this isn't an option in your application.
Now, what we thus need to do is cancel out the original matrix of the parent, which is a two step process. First we need to find the matrix using code along the following lines:
var styles = window.getComputedStyle(el, null);
var matrix = styles.getPropertyValue("-webkit-transform") ||
styles.getPropertyValue("-moz-transform") ||
styles.getPropertyValue("-ms-transform") ||
styles.getPropertyValue("-o-transform") ||
styles.getPropertyValue("transform");
Next the matrix will be a string as shown above which you would need to parse to an array with which you can work (there are jquery plugins to do that). Once you have done that you will need to take the inverse of the matrix (which boils down to rotate(-30deg) in your example) which can be done using for example this library (or your math book :P).
Lastly you would need to do the inverse matrix times (use the matrix library I mentioned previously) a translation matrix (use this tool to figure out how those look (translations are movements along the x and y axis, a bit like left and top on a relatively positioned element, but hardware accelerated and part of the matrix transform css property)) which will give you a new matrix which you can apply to your child element giving you the a translation on the same axis as your parent element.
Now, you could greatly simplify this by doing this with left, top and manual trigonometry1 for specifically rotations only (bypassing the entire need for inverse matrices or even matrices entirely), but this has the distinct disadvantage that it will only work for normal rotations and will need to be changed depending on each specific situation it's used in.
Oh and, if you are now thinking that flash was a lot easier, believe me, the way the axis are rotated in HTML/CSS make a lot of sense and if you want flash like behavior use this library.
1 This is what Mohamed Ali is doing in his answer for example (the transformOffset function in his jsFiddle).
Disclaimer, it has been awhile since I have been doing this stuff and my understanding of matrices has never been extremely good, so if you see any mistakes, please do point them out/fix them.
For Webkit only, the webkitConvertPointFromPageToNode function handles the missing behavior:
var point = webkitConvertPointFromPageToNode(
document.getElementById("outer"),
new WebKitPoint(event.pageX, event.pageY)
);
jsFiddle: http://jsfiddle.net/kB4ra/108/
To cover other browsers as well, you can use the method described in this StackOverflow answer: https://stackoverflow.com/a/6994825/638544
function coords(event, element) {
function a(width) {
var l = 0, r = 200;
while (r - l > 0.0001) {
var mid = (r + l) / 2;
var a = document.createElement('div');
a.style.cssText = 'position: absolute;left:0;top:0;background: red;z-index: 1000;';
a.style[width ? 'width' : 'height'] = mid.toFixed(3) + '%';
a.style[width ? 'height' : 'width'] = '100%';
element.appendChild(a);
var x = document.elementFromPoint(event.clientX, event.clientY);
element.removeChild(a);
if (x === a) {
r = mid;
} else {
if (r === 200) {
return null;
}
l = mid;
}
}
return mid;
}
var l = a(true),
r = a(false);
return (l && r) ? {
x: l,
y: r
} : null;
}
This has the disadvantage of not working when the mouse is outside of the target element, but it should be possible to extend the area it covers by an arbitrary amount (though it would be rather hard to guarantee that it covers the entire window no matter how large).
jsFiddle: http://jsfiddle.net/kB4ra/122/
This can be extended to apply to #point by adding a mousemove event:
$('#outer').mousemove(function(event){
var point = convertCoordinates(event, $("#outer"));
$("#point").css({left: point.x+1, top: point.y+1});
});
Note that I adjust the x and y coordinates of #point by 1px to prevent it from being directly underneath the mouse; if I didn't do that, then it would block dragging #inner. An alternative fix would be to add handlers to #point that detect mouse events and pass them on to whichever element is directly underneath #point (and stopPropagation, so that they don't run twice on larger page elements).
jsFiddle: http://jsfiddle.net/kB4ra/123/
It seems to me that if you do not rotate the div, the div exactly follows the mouse.
This might be a problem with the plugin..maybe you could simulate the draggable function corretly?
This basically will do what you need though it is buggy. Bind the drag event handler, intercept the ui object and modify it to use the offset X and Y of the parent element. All of the X, Y, top, left etc. are in those objects. I will try to get you a better example sometime when today when I get a bit more time. Good luck!
http://jsfiddle.net/kB4ra/107/
may be this is issue of your jquery library or you can check this by assigning z-order value of inner div and outer div make sure that you give higher number to inner div.

What is the most efficient way to reset the size of a shape after scaling in PaperJS

I am attempting to create a very simple beacon-like animation in Paper JS. The idea is that a circle starts off very small and totally opaque and then gets larger and more transparent until it disappears and the animation restarts.
I'm using scaling to make the image larger but resetting it to it's original size is becoming problematic and at the moment I have resorted to cloning a second circle to reset it rather than just working with a single shape, there has to be a simpler way of doing this.
I've create a jsFiddle to demonstrate my rough code so far, any help would be appreciated.
http://jsfiddle.net/colethecoder/Y3S9n/1
Paperjs does not store the original Path, nor does it remember any operations that have been applied to reach the current state, so it can be difficult to reset to a previous state. The easiest approach is to use the this.scale that your current code is calculating and when you want to reset do this.circle.scale(1/this.scale); Here is a jsfiddle that way.
FYI, here is the code path for scaling:
Item.scale()
Item.transform()
Item.apply()
Path._apply()
Segment._transformCoordinates()
So the end result is that _transformCoordinates() is called on each of the four segments in the circle, and it simply moves the point coordinates...nothing is remembered to "undo" the scaling later.
Alternatively to remembering the scale yourself, you can use the Path.fitBounds() function to shrink the circles to an arbitrary size...for instance you could save the bounding rectangle right after creating the Circle, and then fitBounds back to that size.
Set item.applyMatrix = false if you don't want to persist transformations alongside item.
For example, the following code linearly (i.e. "additively") animates item.scaling:
var item = new Path.Rectangle({
point: [75, 75],
size: [5, 5],
strokeColor: 'black',
applyMatrix: false
});
function onFrame(event) {
item.scaling += 0.1;
}
The way i approached this issue was attaching a new object called originalBounds to the paper.js shapes immediately after their instantiation. If i needed to play with its size, coming back its original one became fairly trivial.

Categories