SVG text hit-test - javascript

I'm trying to implement collision detection for SVG text elements using client side JavaScript. The hit-test should check if any glyph of a text overlaps any glyph of another text element. Since getBBox and getExtentOfChar are anything than accurate I need a custom solution.
My first approach was to get the colour of each coordinate/pixel of an element and do the hit-testing manually, but this does not work because it isn't possible to get the colour of a coordinate. It would require an additional canvas to get pixel colours -> awful workaround.
Now I'm thinking about converting the text or the glyphs to polygons for hit testing. Is it possible? Or has anyone another approach for glyph based hit testing?
Best Regards

You are really entering a world of pain and cross browser problems. I ended up doing custom path-rendering of fonts only to get the total text length reliable and consistent. I don't even want to think about glyph-hitting.
One problem for example is that firefox (at least 3.6) and iirc also some version of opera has some rounding error when scaling so when you scale the parent-element holding the text and scale the text by the inverse of that scale, then the letter-spacing will be slightly different compared to without any scale. (Because each letter must begin on an even number or something like that, problem can be solved by multiplying both the upscale and downscale with like 10000 but that's another story)
The performance impact by using path compared to text is unfortunately quite noticeable. If your canvas does any form of animated panning or zooming you should switch to pure text-elements during the animation and once static, turn on path rendering for accuracy.
Fortunally converting svg-fonts to paths is very easy, it is plaintext and using the exact same format as the path-element. (beware of font-embedding-licenses though! Also keep file size in mind as you cannot use the fonts from the users system, )

As for the pixel-based hit-testing – if you switch to HTML5 Canvas, then this will become possible. Several projects provide easy transition from SVG to Canvas, e.g. fabric.js. See a comparison table here.
As for the polygon-based approach – possible, but difficult. You can convert text or glyphs to polygons (paths) using some tool (Inkscape's text-to-path for instance). And then there'll be calculations. Making a general solution for any text will require a lot of work. However, if the text doesn't change, then drawing your text manually using paths can be a quick and dirty solution.

Related

Color-tracking in a WebGL and JavaScript application

My problem seems to be a very simpel problem but I can't really find a good solution.
I'm working on an application that detects motions in an webcam stream. The plugin is written in JavaScript and WebGL. To this end it works fairly good.
I want to extand the application with color-tracking and ultimatley object recogniztion.
For now the color detection simply pass the a given color and the camera texture to a shader. The shader converts the texture and color to CIELAB space and checks the euclidean distance(on the A and B axes,Not the luminance component). If it is within the given distance the texture keeps the color, else the fragment is set to black. The result is barely "OK".
So my question is, is there a more robust and better way to find these colors?
I choosed the CIELAB space since it is somewhat invariant to shadows etc.
EDIT:
Seems that the biggest problem is that I use a Guassian Filter to reduce noise in the video image, this leads to a darker image. Even though LAB is, as stated "somewhat invariant to shadows etc" it makes the detection less efficient. So I'm guessing I need a different way to reduce noise in the image. And I have only tried a spatial median filter that uses the last 5 frames. But it is just not good enough. So an better solution for noise reduction would be MUCH appreciated.
Best regards
You need to normalize the Gaussian filter kernel values to prevent darkening/brightening of the image - you may take a look at this gaussian blur tutorial. You can also try Symmetric nearest neighbor or Kuwahara filters.
Regarding the color tracking, it seems to me you can also use HSV color space.

Javascript/JQuery Calculation of Dimensions

I am looking to achieve something like this. A HTML view has a finite number of images (shown as red boxes in the image below). Are there any browser/jQuery APIs available today (cross-browser) which will let me calculate the dimensions of the remaining space (shown in green boxes) quickly? In the example shown below, it is easy to calculate the green area dimensions using simple geometry given the dimensions of the red boxes. But I am talking about very complex scenarios and complicated combination of images.
Appreciate any help. Thanks.
If you every images have absolute property, you can calculate dimension through top and left properties like $('#elementID').offset().top and $('#elementID').offset().left
From my experience working with DOM element dimensions, you cannot rely on them for exact values, and certainly can't really on them for the same values cross-browser. You can get OK results, but if you have complex scenarios then you will probably come undone at some point.
One way I have achieved similar things in the past is by drawing images to HTML5 Canvas. Using canvas you can have very fine-grained control. I have even iterated canvases pixel-by-pixel to get pixel perfect measurements of items on the canvas.
Check out this tutorial for a brief overview of drawing an image.
UPDATE
There is no easy way to do it. Using this method is low-level and will require you to use mathematics, and possibly byte-level image data from the canvas. However, if your problem is as complex as you suggest then you will have to get stuck in. When I did something similar I was also looking for an easy way to achieve what I wanted in the browser, then spent a month getting to grips with the canvas API, learning about byte-level colour data etc, but in then end I got what I needed, and ended up with something quite unique as it was difficult to achieve in a browser.
To get started, first I would say look at implementing a layered canvas by absolutely positioning multiple canvases on top of each other, then drawing a single image on each one. You already know the sizes of the images, and you can decide the coordinates of where to draw the image, so that's a start. In fact that may be all you need, you can track each image as you draw them by storing coords and dimensions, and you should be able to build up an accurate picture in numbers of where all your images are in 2D space.
Using those numbers you should then be able to calculate any empty spaces on there. However, that is a beyond me and probably a question for Mathematics Stack Exchange (which is actually down at the moment :D).

Javascript "Jigsaw puzzle" with irregular .png shapes

Soooo, what I'm trying to do is essentially a jigsaw puzzle. On the left side is an area with a stack of a dozen or so overlapping .png files of "ripped" paper pieces (all different irregular shapes with alpha) that when put together, in order on the right side, form a sheet of paper with notes on it. A visual (disregard rotated pieces):
Nothing new I know, but my two main caveats in developing this are that it has to work in ie8+ and on touch devices. So this means no flash (where this would of been very easy) and no canvas (stupid ie); which leaves me with js and maybe svg?
The drag and drop part in js is easy enough but the issue I'm having is that, as I'm sure you know, the alpha channel is ignored and the irregular ripped piece is actually a rectangle. Which makes this unusable given that pieces are a stack with pieces overlapping each other.
I can do granular hit detection on the drop by using an image map and setting a variable on rollover.
I've looked around a lot (here + google) and tried some ideas but I'm unable to solve the irregular shape select/drag issue. Any ideas?
Thanks for your time.
Low-tech solution
Nested elements with absolute position
If you're willing to spend a little extra time, there's a way to achieve this fairly closely, without having to use Flash, Canvas, SVG, or even image maps. And unlike an image map, it allows you to nest related content inside each hotspot if needed (e.g., pop-ups).
In the simplest case, you could use a single rectangular hyperlink hotspot for each piece of the puzzle. Obviously that greatly limits the range of shapes you can support (without interfering with overlapping elements).
But, if you take that hyperlink tag and give it a number of span tag children, and give each one absolute position (relative to the hyperlink), and apply the appropriate portion of the image to the background, then you can "construct" irregular image shapes that occupy a single irregular hotspot, with relatively-little interference with overlapping elements.
In effect, the image (with areas of transparency) is treated as a sprite file, with the hyperlink tag and the child span tags each occupying one portion of the "sprite file". Most of the transparent parts of the image will not be occupied by the hyperlink tag or the span tags.
Most shapes can probably be built using a hyperlink tag and 4 - 10 spans. Granted, the more irregular the shapes are, the more spans it will tend to require.
I've done this before, to create hotspots for each of the states on a US map, without using an image map (or Flash, Canvas, SVG), and it wasn't nearly as problematic as you'd think. It just requires a bit of time to figure out the details of how to break up each shape into the right number of rectangles.
The catch
Rounding error on mobile devices
Here's the catch, and it's a doozy. When a web page is scaled on mobile devices (and a regular page is almost always scaled on smaller devices), that introduces a rounding error that causes the px placement of the hyperlink tag and the span tags to possibly vary by at least 1 pixel horizontally and/or vertically. This also happens if desktop browsers are scaled; it's just that desktop browsers are not often scaled.
What would happen is that you would tend to wind up with 1 pixel or so of separation (or overlap) between the different parts of each shape. In many cases, that will tend to be very obvious and not acceptable visually. And depending on the implementation, the locations can vary by as much as 2px or 3px. When it occurs it's difficult to solve, and there are limits to how much of it can be solved.
Last I checked, Firefox is the only browser that's smart enough about rounding px values on scaled pages to avoid this problem. Hopefully other browsers will eventually support it better, as even simple pages often suffer from rounding errors.
The solution
Separate the images from the hotspots
The rounding error isn't much of a problem with hotspots (where precision isn't important). Where it really causes problems is with images (when you see an image not lining up where it should).
It may be possible to avoid the worst of the image rounding errors by doing the following:
Have one set of HTML code for the hotspots, same as described above, except don't display any part of the images in the hotspots. Give them all transparent backgrounds.
Have another set of HTML code for the images. Each one would be a single rectangular element that displays all of the image.
Place each image at the same position as the related hotspot.
Make sure the set of hotspots and the set of images both have the same z-index order. All of the hotspots will be on top of all of the images, but within the hotspots and within the images the order needs to be consistent.
When the hotspot for a piece is dragged, update the position of the related image to keep them at the same location. In effect, the image shadows the hotspot while it's being dragged.
Isn't ExplorerCanvas an option for you? I would think that that would allow you to create a canvas-based solution. That will be much better than anything done with plain html element manipulation through javascript

Pie, bar, line: SVG/VML better than Canvas

I need to choose a library for "standard" charting: pies, lines and bars.
From what I've read, it seems to me that the best format is SVG/VML, like Highcharts for example. SVG is becoming standard across all major browsers, now that IE 9 accepts it. It seems easier to rescale and export than Canvas.
Still, I see that several charting libraries rely on Canvas. Am I missing something? Is there any reason for considering Canvas over SVG for such applications?
You can generally achieve the same results with either. Both end up drawing pixels to the screen for the user. The major differentiators are that HTML5 Canvas gives you pixel-level control over the results (both reading and writing), while SVG is a retained-mode graphics API that makes it very easy to handle events or manipulate the artwork with JavaScript or SMIL Animation and have all redrawing taken care of for you.
In general, I'd suggest using HTML5 Canvas if you:
need pixel-level control over effects (e.g. blurring or blending)
have a very large number of data points that will be presented once (and perhaps panned), but are otherwise static
Use SVG if you:
want complex objects drawn on the screen to be associated with events (e.g. move over a data point to see a tooltip)
want the result to print well at high resolution
need to animate the shapes of various graph parts independently
will be including text in your output that you want to be indexed by search engines
want to use XML and/or XSLT to produce the output
Canvas isn't needed unless you want heavy manipulation/animation or are going to have 10,000+ charts. More on performance analysis here.
It is also important to make the distinction: Charting and diagramming are two different things. Displaying a few bar charts is very different from (for instance) making diagramming flowcharts with 10,000+ movable, link-able, potentially-animated objects.
Every SVG element is a DOM element, and adding 10,000 or 100,000 nodes to the DOM causes incredible slowdown. But adding that many elements to Canvas is entirely doable, and can be quite fast.
In case it may have confused you: RaphaelJS (in my opinion the best charting SVG Library) makes use of the word canvas, but that is no way related to the HTML <canvas> element.
In the past two years, my preference has been to use svg, as I mainly deal with relatively small datasets to build pies, column charts or maps.
However one advantage I have found with canvas is the ability to save the chart as an image thanks to the toDataURL method. I haven't found an equivalent for svg, and it seems that the best way to save an svg chart client side is to convert it to canvas first (using for example canvg).

HTML5 Canvas: How to fake globalCompositeOperation="darker"

I've googled and googled about this, and all I can find, including on StackOverflow, is "support was and is broken in most major browsers." Not an actual solution to my problem.
This month's Playboy came with a pair of 3D glasses (red/cyan) to view the eye-popping centerfold. Naturally, I hit the Internets to find every single red/cyan anaglyph I could and look at how awesome they are. Eventually I found some animated GIFs, which led to the idea that maybe I should make some cool HTML5 Canvas thing that lets you put shapes on a scene in 3D.
This is how far I got. Only works well in Google Chrome. In Firefox, the "Elevated Text" should look correct, but not the rectangles.
The way I'm generating the scene is thus: There are layers that each contain a Z-index, and you can place a rectangle or some text on whichever layer you want. The concept is simple. When drawing the object, it draws one [Z-index] pixels to the left in pure red, then it draws one [Z-index] pixels to the right in pure cyan.
In theory, the overlapping parts should subtract to become pure black. In Chrome, this happens for filling rectangles, stroking text, but not for filling text. In Firefox, this only happens for stroking text.
Although the intended effect of globalCompositeOperation="darker" should do exactly what I want, it's obvious that going down this road is going to bring nothing but pain.
Does anyone here have an idea as to how I can get the effect I want without using globalCompositeOperation? I tried messing with the alpha channel on the colors but didn't really like how that came together (they never add up to pure black). I could draw a third black rectangle between the red and cyan ones, but that doesn't solve the problem for text or arbitrary shapes.
I could do the pixel-for-pixel rendering myself in the Javascript, but that just seems like overkill. Any thoughts?
If you still need this, I have written a free context-blender library that lets you perform Photoshop-style blend modes between two canvases. I have not yet added 'darker', but you could either:
Fork the project on GitHub, add your own support for darker (it's pretty easy to see how to add a mode) and then send me a pull request, or
Ply me with promises of upvotes to get it added for you. :) The only hard part (as with many of the blending modes) will be attempting to determine what is correct when blending one or two areas which are <100% opacity.
It seems that the correct mode in Firefox is globalCompositeOperation="difference". Haven't tested in Chrome or IE.
Because "difference" is a mathematical operation, there is no ambiguity in the implementation, unlike the subjective term "darker".
Maybe you would like to use darken instead of darker. darker has been removed from the specification in 2007
It's a bit of a hacky way but it worked for me.
You can invert the entire canvas by doing
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
Then render whatever you want to render using globalCompositeOperation = "lighter". Then invert the entire canvas again and it should give the same results as a "darker" blend mode would.

Categories

Resources