Issue 1:
Description of the problem

Given the code below, only one request should be fired.

THREE.Cache.enabled = true
var fileLoader = new THREE.FileLoader();
fileLoader.load('foo.jpg'); // A
fileLoader.load('foo.jpg'); // B

A checks the cache and sees no pending requests.

A makes the request and stores the pending request in the cache as a Promise. The Promise will resolve the file.

B checks the cache and sees a pending request. B waits for the Promise to resolve the file.

The request finishes, and the Promise resolves.

A and B, both waiting on the Promise, triggers their callbacks at the same time with the same file response.

I'd like to volunteer to work on this. In A-Frame, we often have been having to maintain a cache of promises to prevent duplicate requests. If it sounds reasonable, below are several possible implementations:

Change THREE.Cache to store promises that resolve files, rather than directly storing files. I think this is the optimal solution because requests will always be asynchronous. This is sort of a breaking change, but three.js applications don't often enable the cache, or don't care about the internals of the cache.

Create a separate cache object (RequestCache) for pending requests. The current Cache stays intact, but there is more logic to maintain both the pending and complete caches.

Keep the pending request cache internal to the FileLoader. It'll just be a variable var requestCache = {}.

We'd also need a Promise polyfill. They're less than 1KB gzipped if that sounds fine.

Three.js version

Dev

r84

Browser

All of them

OS

All of them

Hardware Requirements (graphics card, VR Device, ...)

N/A

Issue 2:
Description of the problem

If a Hole in the form of one or more absarc/absellipse/arc/ellipse segments forming a full circle or ellipse is added to a 2D Shape and rendered as a ShapeBufferGeometry, this can wreck the Shape, anything from removing two triangles between one vertice in the outer Path and the three vertices around an absarc/absellipse/arc/ellipse start/end, to not rendering the Shape at all, depending on the Shape outer Path geometry. It gives the warning "THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()". This does not happen if the Hole is a single closed bezierCurveTo or any other Curve segments, independent of what Curve segments make up the outer Path of the Shape. It does however give a warning for each Path start/end "THREE.ShapeUtils: Duplicate point".

All vertices are correctly added to the geometry position attribute, but the problem seems to be with vertex indices, which are added oddly, incomplete or not at all, depending on the Shape outer Path. The fact that the glitch always seems to emanate around an absarc/absellipse/arc/ellipse start or end indicates that it may be the wrapping there working incorrectly. The actual reason seems to be that the start and end points at the angles 0 and 2 * Math.PI can become unequal, because Math.sin( 0 ) === 0 but Math.sin( 2 * Math.PI ) === -2.4492935982947064e-16, so the "duplicate" end point is not removed following an equal comparison (ShapeUtils.js line 38-40). In some cases, this still works, because rounding errors compensate for the sin error.

There are several ways to solve this. The probably most correct one, to handle any kind of rounding errors, would be to replace the straightforward function

Vector3.equals = function ( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
}

with a more complex function similar to

Vector3.approximates = function ( v, re ) {
// re = permitted relative error
return (
Math.abs( v.x - this.x ) / ( 1 + Math.abs( v.x ) + Math.abs( this.x ) ) <= re &&
Math.abs( v.y - this.y ) / ( 1 + Math.abs( v.y ) + Math.abs( this.y ) ) <= re &&
Math.abs( v.z - this.z ) / ( 1 + Math.abs( v.z ) + Math.abs( this.z ) ) <= re
);
}

or a joined function, keeping (most of) the simplicity and speed in the simple case

Vector3.equals = function ( v, re ) {
// re = permitted relative error, optional
if ( v.x === this.x && v.y === this.y && v.z === this.z )
return true;
if ( re === undefined || re <= 0 )
return false;
return (
Math.abs( v.x - this.x ) <= re * ( 1 + Math.abs( v.x ) + Math.abs( this.x ) ) &&
Math.abs( v.y - this.y ) <= re * ( 1 + Math.abs( v.y ) + Math.abs( this.y ) ) &&
Math.abs( v.z - this.z ) <= re * ( 1 + Math.abs( v.z ) + Math.abs( this.z ) )
);
}

(edited function to handle origin position case and add joined function)

Three.js version

Dev

r89

...

Browser

All of them

Chrome

Firefox

Internet Explorer

Safari

OS

All of them

Windows

macOS

Linux

Android

iOS

Hardware Requirements (graphics card, VR Device, ...)