Positioning SVG Elements - javascript

In the course of toying with SVG for the first time (using the Raphael library), I've run into a problem positioning dynamic elements on the canvas in such a way that they're completely contained within the canvas. What I'm trying to do is randomly position n words/short phrases.
Since the text is variable, its position needs to be variable as well so what I'm doing is:
Initially creating the text at point 0,0 with no opacity.
Checking the width of the drawn text element using text.getBBox().width.
Setting a new x coordinate as Math.random() * (canvas_width - ( text_width/2 ) - pad).
Altering the x coordinate of the text to the newly set value (text.attr( 'x', x ) ).
Setting the opacity attribute of the text to 1.
I'll be the first to admit that my math acumen is limited, but this seems pretty straightforward. Somehow, I still end up with text running off beyond the right edge of my canvas. For simplicity above, I removed the bit that also sets a minimum x value by adding it to the Math.random() result. It is there, though, and I see the same problem on the leading edge of the canvas.
My understanding (such as it is), is that the Math.random() bits would generate a number between 0 and 1 which could then be multiplied by some number (in my case, the canvas width - half of the text width - some arbitrary padding) to get the outer bound. I'm dividing the width of the text in half because its position on the grid is set at its center.
I hope I've just been staring at this for too long, but is my math that rusty or am I misunderstanding something about the behavior of Math.random(), SVG, text or anything else that's under the hood of this solution?

The answer turned out to be how I was thinking about the Math.random() equation. It's not quite as simple as multiplying by the max and then adding the minimum value (of course). It's really more like establishing a double wide gutter on the right end of the container and then shifting the entire boundary to eat up half of that gutter:
var x = Math.random() * ( canvas_w - 20 - ( text.getBBox().width ) ) + ( text.getBBox().width/2 + 10 );
In English...
You have to double the width of each element you want to account for so you can shift the entire range back by that width to keep things nice and equal. In my case, I want to account for half of the width of the text plus a padding of 10.
For example...
Given a canvas width of 500, a text width of 50 and a desired "gutter" of 10, I create a random number between 0 and 430 (500 - 20 - 50). By adding back the widths I need to account for--half of the text width (25) + the padding (10)--I'm left with a random number between 35 and 465. If my text sits at the outer edges of that boundary, it can only reach as far as the 10 or 490.
Hopefully that's clear enough to make sense. Although it makes sense when I think about it, this kind of thing isn't immediately intuitive to me, so I'm sure I'll be referring back here often.

Related

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.

Complex mathematical horizontal parallax easing function

I'm currently diving into parallax effects on the web.
Parallax scrolling is a technique in computer graphics and web design, where background images move by the camera slower than foreground images, creating an illusion of depth in a 2D scene and adding to the immersion.
~ Wikipedia
I want to create a little container (could be an image, or any block level element) and move it across the screen horizontally as the user scrolls.
The effect should be scalable across all viewports. Meaning that the hight and the width of the element that the element is moving across should not matter.
When the user has scrolled half of the height of the screen the "moving element" should be in the exact center. Since the user will have scrolled half of the screen the element will be vertically already. We're only worried about horizontally right now.
I've thought about this question for a while and came up with a pretty good idea of how.
Take the hight and the width of the element you want the "moving element" to move across. For example a screen that is 1000px tall and 600px wide.
Divide the width by the height. For example (600px / 1000px = 3/5 = 0.6)
Take the amount of pixels the user scrolled and multiply it by the number we just created. For example (500px * 0.6 = 300px). As you can see this is the exact center.
Move the element across the screen by the amount of pixels just calculated.
This calculation works fine even for every screen size, however it's linear. Meaning that the element will move at the same speed across the screen all the time. Let me show you what I mean.
Let's draw out a screen size. (Let's say 1000 * 500)
Calculate two points for this graph ->
screen factor: (500 / 1000) = 0.5
1. The first point is going to be easy. Let's say we scrolled exactly 0px -> (0.5 * 0) = 0
The "Moving element" will not have moved at all.
2. For the second element we'll take the center. Just for convenience.
The vertical center is at 500px -> (0.5 * 500) = 250 px (Exactly the horizontal center)
Put the results in a graph and draw a line through the points.
In the graph above you can see that whenever the user scrolls down the "moving element" will follow the line (the values on the x-axis).
My question
I really hope I described all that well enough to understand. Now on to my question.
What I want to create is a moving element that would go faster on the edge of the screen and slow down a bit in the middle. If we were to draw that out in the same way we just did. (Creating a graph where we can take the amount of pixels scrolled and see where the element should be positioned horizontally) it would look like this:
Sorry for the poor quality of the image but this is the part I'm having problems with.
As you can see in this graph the "moving element" wouldn't be moving all that much in the middle of the graph. (I over did it a bit in my drawing but you get the general idea.)
What I need is a mathematical function that takes three parameters (Screen height, width and the amount of pixels scrolled) and returns the horizontal position of the "moving element".
My idea:
My idea was to position the element in the dead center of the page and then to move it left and right (translations using CSS and JavaScript) based on how far there has been scrolled.
The graph would look something like this:
The (Hand drawn) graph above would be true for a screen that's 1000x600px since the "moving element" translates -300px when no scrolling has been done and 300px when 100% has been scrolled.
However I have no idea on how to create a mathematical function that would be true for every screen size.
To be clear I need a function that "always" starts at Y= (-screen-width/2) and X = 0. It should always cross the point (sreen-height; (screen-width//2)) and the function should be in a form of x^3 (To get the right easing)
I really hope I explained myself well enough and I really hope somebody can help me out here.
Answer from math.stackexchange
Besides asking my question here I also went ahead and posted this question on math.stackexchange.com. Stackoverflow's mathematical sister site. Somebody there helped me find an answer to my question.
The function had to be a function that would output the moving-element it's horizontal position in pixels relative to the horizontal center of the page based on the amount of pixels scrolled since the element was first visible. The function would have to be "steeper" on the edges and ease into a short stop in the middle and be usable across every possible screen size. Meaning that the mathematical function would have to be positioned based on two variables, screen height and -width.
The answer they came up with on math.stackexchange:
In this example s-width is the width of the screen in pixels. s-height is the height of the screen in pixels. px-scrolled is the amount of pixels scrolled since the element was first visible.
The output pos is the moving elements horizontal position in pixels relative to the center of the screen.
If you put all this math into JavaScript you get the following:
var pos = ((4*win_width)/(Math.pow(win_height, 3))) * Math.pow(px_since_visible - (win_height/2),3)
There is a working example available on codepen. You can find it here.
You can position it with a function f that actually draws that trajectory.
This is the idea I propose:
Create the function trajectory f such that f(0) = 0, and f(1) = 1 (add more constraints in order to reproduce the effect you are looking for, ex: f(0.5) = 0.5)
Within each scroll event, set x as the amount scrolled and position the element using the coordinates (f(x) * (w - s), x * (h - s)), where w is the document width, h is the document height and s is the size of the element
I can see that cubic functions are plotted like the trajectory you want, so I've been testing with different functions and I've got this working example https://codepen.io/anon/pen/YZJxGa
var element = $('.element')
var height = $(document).height()
var scrollable = $('body').height() - $(window).height()
var width = $('body').width();
$(window).scroll(function () {
var winH = $(window).scrollTop()
var x;
// Determine the amount of px scrolled since the element first came into view.
console.log(winH, scrollable)
x = (winH/scrollable) > 1 ? 1 : (winH/scrollable);
var posY = x * (height - 120);
var posX = (-1.000800320128*x
+6.0024009603841*x**2-4.0016006402561*x**3)*(width - 120)
console.log(posY)
if (x > 0) {
console.log(`translate(${posX}px, ${posY}px, 0)`)
element.css({
'transform': `translate3d(${posX}px, ${posY}px, 0)`
})
}
})
You can generate more cubic functions using this tool I've just found http://skisickness.com/2010/04/28/ or solve a couple of systems of linear equations using the fact that you just want to find values for a, b, c and d for f(x) = ax^3 + bx^2 + cx + d

How to get pixel position

I feel really dumb asking this, but bear with me. I know about the formula to get the pixel position in a linear array:
pos = (y * width + x) * 4
which works fine. jsFiddle. But before the image/table is linearized, same formula doesn't work. You need to use (let's discard the RGBA for simplicity)
pos = (y-1) * width + x
Why is that? I'm probably missing something really simple.
Update: I knew it was something simple. Silly me.
In javascript pixel coordinates start at 0, the same as any coordinate system. The pixel is referenced by its top left corner, thus the first pixel is at (0,0) , then the next going right (1,0) (2,0) and so on. The pixel below at (0,1) we give coordinates relative to the origin (0,0).
We give sizes as counts. When using width and height they are pixel counts and you start at one when you count. So the 100th pixel is on row 99. Just the same as this is the 21th century but we are in the year 2015.
So no need to subtract 1 from the pixel coordinates.

Calculate new width when skewing in canvas

I'm using canvas for a project and I have a number of elements that I'm skewing. I'm only skewing on the y value and just want to know what the new width of the image is after skewing (so I can align it with another canvas element). Check out the code below to see what I mean
ctx.save();
//skew the context
ctx.transform(1,0,1.3,0,0,0);
//draw two images with different heights/widths
ctx.drawImage(image,0,0,42,60);
ctx.drawImage(image,0,0,32,25);
The goal would be to know that the 42 by 60 image was now a X by 60 image so I could do some translating before drawing it at 0,0. It's easy enough to measure each image individually, but I have different skew values and heights/widths throughout the project that need to be align. Currently I use this code (works decently for images between 25 and 42 widths):
var skewModifier = imageWidth*(8/6)+(19/3);
var skewAmount = 1.3; //this is dynamic in my app
var width = (skewModifier*skewAmount)+imageWidth;
As images get wider though this formula quickly falls apart (I think it's a sloping formula not a straight value like this one). Any ideas on what canvas does for skews?
You should be able to derive it mathematically. I believe:
Math.atan(skewAmount) is the angle, in radians, that something is skewed with respect to the origin.
So 1.3 would skew the object by 0.915 radians or 52 degrees.
So here's a red unskewed object next to the same object skewed (painted green). So you have a right triangle:
We know the origin angle (0.915 rads) and we know the adjacent side length, which is 60 and 25 for your two images. (red's height).
The hypotenuse is the long side thats being skewed.
And the opposite side is the triangle bottom - how much its been skewed!
Tangent gets us opposite / adjacent if I recall, so for the first one:
tan(0.915) = opposite / 60, solving for the opposite in JavaScript code we have:
opposite = Math.tan(0.915)*60
So the bottom side of the skewed object starts about 77 pixels away from the origin. Lets check our work in the canvas:
http://jsfiddle.net/LBzUt/
Looks good to me!
The triangle in question of course is the canvas origin, that black dot I painted, and the bottom-left of the red rectangle, which is the original position that we're searching for before skewing.
That was a bit of a haphazard explanation. Any questions?
Taking Simon's fiddle example one step further, so you can simply enter the degrees:
Here's the fiddle
http://jsfiddle.net/LBzUt/33/

How to know what lines / chars are currently visible in a textarea?

Online editor with "live preview": there is a textarea on the left and a preview div on the right. Whenever the textarea changes, the preview is updated.
This works well for small documents; for very long documents however, it becomes sluggish, because the preview has lots of DOM elements that are constantly repainted.
It would be better to only send to the preview, the part of the textarea that is currently visible (since it's the one that needs to be previewed).
There is a heuristic way to get the first visible line of the textarea:
determine current scroll offset of the textarea:
offset = scrollTop / scrollHeight
(0 < offset < 1)
the first line that is visible in the textarea is:
(total number of lines) x offset
However this only works for "short" lines, ie lines that don't wrap. In general, the number of "lines" in a textarea is not the number of linebreaks; a long line, without linebreaks, wraps and might occupy many "line spaces".
One could try to calculate the average number of "line spaces" a line occupies (average number of characters between line breaks, width of textarea, size of font...) but this is wildly imprecise.
Is there a way to know the position of the first and last visible characters in a textarea?
Well, as a crazy way for doing it you can look how ACE converts the text into canvas-drawn lines. I assume with this approach you can determine the exact position (or better to say, the exact line objects that are currently visible.
But this could also be a kind of vicious circle if the canvas-generated text is compatible in complexity to what you are having in the preview.
Alternatively you can use a fixed-width font which will give you a knowledge of the exact number of chars in the single line, and thus a way of calculating the exact first / last lines.

Categories