Why are my SVG bezier curves broken in Firefox? - javascript

I came across a problem today where diagrams I have created are not working in Firefox when created via getPointAtLength. Here is a fiddle showing the problem:
http://jsfiddle.net/xfpDA/9/
Note comments at the top of the javascript.
The relevant part of the SVG path is:
C189.5,423.237,266.965,390.696,266.965,390.696
This works perfectly in Chrome and IE, but Firefox skips right over the curve and just closes the path.
However, a tiny change to the curve is enough to fix the problem:
C189.5,423.236,266.965,390.696,266.965,390.696
^
Why does changing that value by a thousandth make the difference between a pretty curve and a broken SVG?
EDIT: That coordinate is not the only one that can be changed to 'fix' the problem, so it appears that Firefox does not play nicely with high-precision curve values.

Firefox is not able to calculate the length of the curve under some circumstances (e.g. delta=0/0). So the result for that path part is NaN/0/undefined and therefore is not added to the total length and the domain for getPointAtLength is smaller.

Related

Three JS - Strange raycast behaviour when objects are moved

Version and info
THREE.ObjectLoader2: 2.4.1
THREE.LoaderSupport.MeshBuilder: 1.2.1
THREE.LoaderSupport.WorkerSupport: 2.2.0
THREE.WebGLRenderer: 93
THREE.REVISION: 93
The problem
When I raycast an object in my scene, I found that it worked perfectly down to the pixel, until I moved the object. In my program I expload the scene, so I move all the objects, and child objects away from the center of the scene.
To easily visualise the issue instead of raycasting a single point at a mouse click, I opted to raycast the entire screen, this is what I get (Figure 1)
(Figure 1)
The reason for the gaps is because it took to long to raycast every pixel, so instead I raycasted every fourth. The reason for the gap in the middle is because I zoomed away from the original position.
Now, see what happens when I expload the object (Figure 2),
(Figure 2)
As you can see, there is almost a circle. Why is this?
What I've tried
I've tried many things across the internet, and came here when I could find no more.
I've tried a range of different models, some work differently to others, strangely enough. The lamborghini-aventador which was created in Blender works the strangest.
To see if it was a problem with the exploading code, I moved the object to the right. This is where things get interesting (Figure 3).
(Figure 3)
It looks as if my outlining I put on the object (the outlines are an EdgesGeometry) is behind, the actual object is in the middle, and the raycasts are further.
What I speculate
I suspect the issue is to do with scaling. So I tried removing all scaling I did in the code, however I got the same result, unfortunately.
Apologies if this is some noobie mistake, though I do hope it is :)
The code
For those who are adventurous enough to delve into my terrible code base, here it is (the majority of the code is inside demo.js):
github
Testing it
Press G to shoot the raycasts (will freeze for a bit), press X to expload, press S to unexpload. Standard orbit controls.
What I've found
Here are some of the links I have already found and tried on this issue:
https://threejs.org/docs/#api/core/Raycaster
Three.js Raycaster not detecting scene mesh
https://github.com/mrdoob/three.js/issues/1325 (Updating the matrix)
http://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works (Followed step by step)
... and many more ...
Any ideas?
I think that your model might not have proper bounding boxes/spheres generated.
The circular shape could result from the rays passing the bounding sphere check of a bounding sphere that is too small.
You mention resizing/processing your geometries in some way... After you do that, try calling geometry.computeBoundingBox() and geometry.computeBoundingSphere() to rebuild boxes and spheres, and see if that helps?
edit: Apparently this problem was due to bounding boxes and spheres not being recomputed...
the fix was to:
scene.traverse( (o)=>if(o.geometry){o.geometry.computeBoundingBox();o.geometry.computeBoundingSphere();} );

D3 map SVG performance

I've been struggling the past few days to optimize performance on a D3 map, especially on mobile. I am using SVG transforms for zooming and panning but made the following observation: the overkill comes from path strokes used to fake spacing between countries.
I have uploaded a pair of sample maps for comparison:
http://www.nicksotiriadis.gr/d3/d3-map-1.html
http://www.nicksotiriadis.gr/d3/d3-map-2.html
The only difference between the two maps is the stroke path along the country paths, and the difference in performance is even noticeable on desktop devices - but more obvious on mobile. Removing the path strokes makes mobile performance a breeze..
I tried all kinds of svg stroke shape-rendering options without significant results.
Now to the question. Is there any way to remove a thin border from each country to fake the spacing between countries instead of using a stroke?
If anyone else has a different suggestion I'd love to hear it!
Update: Attaching explanation photo.
What I have drawn is this. The red arrow points to the country joints. When adding a stroke in a color same as the background to the country paths (here depicted in dark grey color) it creates the sense that the countries are seprated - however this adds a serious performance hit on mobile devices. What I am looking for is somehow re-shape the countries paths so that their borderlines are where the blue arrow points, but without having a stroke.
Update 2: People seem not to be able to understand what I am looking for, so I am updating this in order to make the question even clearer.
Let's assume that the original countries paths are shown on the left of this image. What I am looking for is a way that I can somehow 'contract' the paths inwards so that the newly created paths shown in red, leave enough empty space between them that will 'emulate' a stroke between them.
Doing this, will leave no use to having an extra layer of strokes, thus gain performance from only using paths instead of paths+strokes.
Update 2: Hello again, I seem to have found a half-solution to my problem. I managed to extract the topojson to shapefile, edit the shapefile the way I want (used a program named OpenJump), but the conversion takes away all the topojson properties I need - id, country name, so I can't convert back to the original topojson.
Does anyone have any suggestions?
D3 has a thing just for that: topojson.mesh() (see documentation). The idea is that since most countries share borders, there's no need to draw the shared borders twice. If you can draw each border only once, you get as much as 80% reduction in the number of strokes you have to draw. The mesh method does the javascript processing to turn a bunch of closed shapes (countries) into the multiline path of just the borders between them. You can then draw that multiline path into a single <path> object that you position on top of the fills.
The mesh looks like this.
Here's another example.
Finally found the answer. This radically improves d3 map performance!
1) I got my topojson file and extracted to shapefile using mapshaper.org. This gives 3 files: .shp, .shx, .dbf . From what I realized the .dbf file holds all the TopoJSON properties/attributes.
2) Opened the .shp shape file to OpenJUMP http://www.openjump.org/ - Which automatically imports the .dbf file as well.
3) I selected the countries layer and went to Tools > Analysis > Buffer.
4) Checked the Update geometry in source layer box so that the geometry is edited without losing the rest of the attributes/properties and added a negative Fixed Distance -0.1. This shrinked all the country geometries to the result I was looking for.
5) Saved Dataset as ESRI Shapefile
6) Reimported BOTH .shp and .dbf that were produced from OpenJUMP back to mapshaper.org - careful, BOTH files.
7) Exported as TopoJSON. Contains new shape and all original properties/attributes!
The following link has been updated with the new produced map; we have a 'bordered' look without the need of strokes.
http://v7.nicksotiriadis.gr/d3/d3-map-1.html
Compare the performance to this link that has the original shapes + stroke. Please try on mobile to see the performance difference!
http://v7.nicksotiriadis.gr/d3/d3-map-2.html
Also, here is the updated world map TopoJSON file in case someone wants some extra performance! :D
http://v7.nicksotiriadis.gr/d3/js/world-topo-bordered.json
There might be a couple of reasons of this behaviour (on my computer, everything is working fine at the same speed ):
Browser
Which browser do you use ? On Chrome, your exemples are working perfectly.
TopoJson
eg. previous answer.
Animation
You are launching the animation when the page is loading. You might want to add a delay (animation().delay(in ms)). There is also a function in D3: queue(), https://github.com/mbostock/queue which load the data before launching a function.
--
If none of this change your problem, and if you want it to work fine on mobile, you can try to mix D3 and Leaflet (map for mobiles), which is great in term of performance by loading tiles.
One example:
http://bl.ocks.org/zross/6a31f4ef9e778d94c204
Hope it helps

How to eliminate the seam between UV map triangles (HTML5 Canvas)?

I've set up a simple demo of UV mapping at http://jsfiddle.net/pB5A9/1/ . It's based on Image manipulation and texture mapping using HTML5 Canvas? .
The algorithm itself works pretty well. There's one issue, though. There appears to be a faint seam between the triangles. I tested this both on Chrome (14.something) and Safari (5.1). It's possible it works correctly in other browsers though I cannot be sure of that.
I'm guessing the issue has something to do with clipping. The thing is I'm not sure how to tweak the coordinates so that the issue disappears. Perhaps it's missing some padding (+1) or something. Ideas are welcome. :)
It has to do with the way different browsers choose to take on anti-aliasing (or not).
It looks good on Chrome 15.
Clipping less should solve your problem. I'd suggest you try expanding your clipping region for each object by 0.5 or 1 pixel and see how it does. (alternatively you could try shifting everything by 0.5 pixels and seeing if the anti-aliasing changes, you might be surprised)

SVG text hit-test

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.

Canvas code not working in IE9 - probably alpha or globalCompositeOperation issue

I have a javascript routine which is designed to flickering sun rays. The routine creates 'numRays' rays, then begins to overwrite the rays using globalCompositeOperation = 'copy' and drawing a transparent ray over the oldest ray.
You can see the program here:Sun Ray Page
JSFiddle.net code located here:JSFiddle Sun Rays (I'm not sure how to make it work on JSFiddle, so if someone can fix my fiddle, I'd appreciate that, too.)
(Note: I have left a yellow stroke around the erased rays for diagnostic purposes, so you can see them executed.)
The code works in Chrome and Firefox, however, in IE9, the original rays are not drawn when I leave the erase section active. In other words, if I remark out the section that creates the erase stroke, the ray fills occur. If I do not, then only the erase strokes occur.
I have tested various fixed alpha values and IE9 seems to handle them perfectly. I've set both sections to globalCompositeOperation = 'source-over' and still do not get the original rays.
Can someone help me correct this code to work on IE9?
Thanks
It isn't working in jsfiddle because you never call init()
IE9 is probably breaking script execution on the line:
ctx.rotate(rayArray[currentRay]); // Rotate to the rayAngle
Because it throws an error the first 11 times it is called, because you keep trying to access a value that is undefined the first 10-11 times through.
So I moved your incrementing code down to after the ctx.rotate statement and added the init. It doesn't have errors in IE or FireFox, but I have no way of knowing if its what you intended:
http://jsfiddle.net/Y5Y48/18/

Categories

Resources