I am trying to generate a texture from an array in threeJS and it is not working as expected.
It appears that the way I generate the texture is not correct.
If I use the following texture, it works as expected.
http://www.html5canvastutorials.com/demos/assets/crate.jpg
crateTex = THREE.ImageUtils.loadTexture('data/crate.jpg');
If I generate a dummy texture and try to display it, it is all black...
var dummyRGBA = new Uint8Array(4 * 4 * 4);
for(var i=0; i< 4 * 4; i++){
// RGB from 0 to 255
dummyRGBA[4*i] = dummyRGBA[4*i + 1] = dummyRGBA[4*i + 2] = 255*i/(4*4);
// OPACITY
dummyRGBA[4*i + 3] = 255;
}
dummyDataTex = new THREE.DataTexture( dummyRGBA, 4, 4, THREE.RGBAFormat );
dummyDataTex.needsUpdate = true;
dummyTex = new THREE.Texture(dummyDataTex);
I think your mistake is in the fact that you make a texture of a texture.
When you do:
dummyDataTex = new THREE.DataTexture( dummyRGBA, 4, 4, THREE.RGBAFormat );
the object dummyDataTex that you create here is already of type THREE.Texture.
So your next step:
dummyTex = new THREE.Texture(dummyDataTex);
is not necessary. You should instead immediately use dummyDataTex.
Related
Would really appreciate some help updating the webgl-wireframes library code to the latest version of threejs.
This function causes the following errors
Uncaught TypeError: THREE.Geometry is not a constructor
THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry
.setAttribute to replace/resize attribute buffers
Library with implementation: https://github.com/mattdesl/webgl-wireframes.
Thanks to Mugen87, this code now works for me in place of the helper functions with the original libray.
function createGeometry ( edgeRemoval, x_divisions, y_divisions) {
if (mesh.geometry) mesh.geometry.dispose();
geometry = new THREE.PlaneBufferGeometry(3, 3, x_divisions, y_divisions)
geometry = geometry.toNonIndexed();
const pos = geometry.attributes.position
const count = pos.length / 3
let bary = []
const removeEdge = edgeRemoval
for (let i = 0; i < count; i++){
const even = i % 2 === 0
const Q = removeEdge ? 1 : 0
if (even) {
bary.push(0, 0, 1,
0, 1, 0,
1, 0, Q )
} else {
bary.push(0, 1, 0,
0, 0, 1,
1, 0, Q
)
}
}
bary = new Float32Array(bary)
geometry.setAttribute(
"barycentric",
new THREE.BufferAttribute(bary, 3)
)
mesh.geometry = geometry;
mesh.material = material;
}
webgl-wireframes requires non-indexed geometries so barycentric coordiantes can be computed for the wireframe effect. Hence, the project developed the helper function unindexBufferGeometry().
With the latest version of three.js (r128) the library could use BufferGeometry.toNonIndexed() which does not throw the above error. So this line should be:
geometry = geometry.toNonIndexed();
Notice that setArray() was removed because it was possible to use this method to resize buffer attributes. This workflow is not supported anymore since buffer attributes are considered to have a fixed size (for performance reasons). So if you want to resize buffer data, create a new geometry with new buffer attributes.
I would like to have this colour scheme as my input domain:
And a value between 0 and 1 as my output range.
However, I am not sure which type of scale to use or how to add the scheme as an input domain.
The code below is the opposite of what I am trying to do.
let scaleSequential1 = d3.scaleSequential()
.domain([0, 1])
.interpolator(d3.interpolateViridis);
console.log( scaleSequential1(0) ); //#440154
console.log( scaleSequential1(0.5) ); //#21918c
console.log( scaleSequential1(1) ); //#fde725
Following up on my comment, there is no direct way to do this. You can hack it up if you like this. If you are going to call it frequently I'd wrap in in a closure to spare the .map...
function invertViridis(color){
return d3.range(0, 1.01, 0.01).map(i => d3.interpolateViridis(i)).indexOf(color) / 100;
}
console.log(invertViridis("#440154"));
console.log(invertViridis("#3b528b"));
console.log(invertViridis("#21918c"));
console.log(invertViridis("#5ec962"));
console.log(invertViridis("#fde725"));
function invertViridisClosure(){
var r = d3.range(0, 1.01, 0.01).map(i => d3.interpolateViridis(i));
return function(color) {
return r.indexOf(color) / 100;
}
}
let f = invertViridisClosure();
console.log(f("#440154"));
console.log(f("#3b528b"));
console.log(f("#21918c"));
console.log(f("#5ec962"));
console.log(f("#fde725"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I'll suggest a different approach, which deals with D3 source code.
If you look at the d3.interpolateViridis source, you'll see that the colours are just a long string, which is passed to this function:
export default function(specifier) {
var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;
while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
return colors;
}
Then, the above function returns an array of 256 colours.
Therefore, my approach is just using that function (here renamed as getColours) to create our colours array:
const colorsArray = getColors(colorsString);
With that array in hand we can just use indexOf and a basic linear scale, since you explicitly asked for a D3 scale in your question's title. However, we can ditch the scales and use a vanilla JavaScript function:
function invert(color) {
return colorsArray.indexOf(color) / (colorsArray.length - 1);
}
Here is the demo:
const colorsString = "44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725";
const colorsArray = getColors(colorsString);
function invert(color) {
return colorsArray.indexOf(color) / (colorsArray.length - 1);
}
console.log(invert("#440154"));
console.log(invert("#21918c"));
console.log(invert("#fde725"));
function getColors(specifier) {
var n = specifier.length / 6 | 0,
colors = new Array(n),
i = 0;
while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
return colors;
}
Have in ind that, because 256 is an even number, there is no colour that will return exactly 0.5. The other answer has a colour returning a nice and beautiful 0.5 because it uses d3.range(0, 1.01, 0.01), which produces an array with an odd number of elements (101 elements).
I am trying to create a scene for my class where a group of objects (a model) has to be cloned and displayed in random positions. I created a function for this, but unfortunately, it only transfers the same object from one place to another. I need to add more objects instead of moving the same one, but sadly I cannot find any information about it. I tried to clone, but I failed :/ Later on, I will have to remove these models one by one, so if anybody could give advice on that too, I would appreciate it.
Here's my code:
this.addmodel = function() {
scene.add(model);
model.name = "model-" + scene.children.length;
model.position.x= -20 + Math.round((Math.random() * 40));
model.position.y= Math.round((Math.random() * 5));
model.position.z= -17.5 + Math.round((Math.random() * 35));
this.numOfObjects = scene.children.length;
}
Sample object:
function Model(){
this.mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1),new THREE.MeshBasicMaterial({color:"red"}));
this.addModel = function(){
var newModel = this.mesh.clone();
newModel.name = "model-" + scene.children.length;
newModel.position.x= -20 + Math.round((Math.random() * 40));
newModel.position.y= Math.round((Math.random() * 5));
newModel.position.z= -17.5 + Math.round((Math.random() * 35));
scene.add(newModel);
}
};
then create an instance and call its method:
var cubeModel = new Model();
for(var i = 0; i < 10; i++){
cubeModel.addModel();
}
jsfiddle example
I have a rather broad question, but no idea how to tackle that. So forgive me.
I am trying to have several (like 200 and more) objects and, let's just say, a container to the side of the field, where I draw the objects. Now what I want is, that each object has some non visual attributes and when I click on that object, the attributes should appear in that container.
Now I could go about it?
I mean, I know I can ask for the name of the selected object and then do a key value query from some dictionary. Question is, whether there is an easier way to go about it.
For the click event I used a library called threex.domevents, check the GitHub page for more information, the code for the event it's self explanatory.
First domevents needs to be initialized in your scene like this:
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
Then I created a custom Mesh object:
// random id
function genRandomId()
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
// random int for position
var min = -50;
var max = 50;
function genRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// custom mesh --------------------------------------------
function MyMesh(geometry, material, destinationContainer) {
THREE.Mesh.call(this, geometry, material);
this.userData = {
foo1: genRandomId(),
foo2: genRandomId(),
foo3: genRandomId(),
};
this.position.x = genRandomInt(min, max);
this.position.y = genRandomInt(min, max);
this.position.z = genRandomInt(min, max);
var that = this;
// click event listener
domEvents.addEventListener(this, 'click', function(event) {
console.log('clicked object on position:');
console.log(that.position);
destinationContainer.userData = that.userData;
console.log('Now the conainer has:');
console.log(destinationContainer.userData);
destinationContainer.userData = that.userData;
}, false);
}
MyMesh.prototype = Object.create(THREE.Mesh.prototype);
MyMesh.prototype.constructor = MyMesh;
genRandomId and genRandomInt are random generators for the pourpose of illustrating this example, I took the code for the random ids from Generate random string/characters in JavaScript.
In your scene you can generate 200 (or more) MyMesh meshes and add them to the scene:
const color = 0x156289;
const emissive = 0x072534;
var planeGeometry = new THREE.PlaneGeometry(5, 5);
var planeMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
side: THREE.DoubleSide,
shading: THREE.FlatShading
});
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);
var objGeometry = new THREE.BoxGeometry(1, 1, 1);
var objMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
shading: THREE.FlatShading
});
var i = 0;
while (i < 200) {
scene.add(new MyMesh(objGeometry, objMaterial, planeMesh));
i++;
}
And finally render the scene:
var render = function() {
requestAnimationFrame(render);
planeMesh.rotation.x += 0.010;
planeMesh.rotation.y += 0.010;
renderer.render(scene, camera);
};
render();
This is a demo with the full source code: http://run.plnkr.co/plunks/W4x8XsXVroOaLUCSeXgO/
Open the browser console and click on a cube and you'll see the that planeMesh is switching its userData attributes with the ones of the clicked cube mesh.
Yes, that's fine. You can put your own custom keys directly on a Three.js object and it shouldn't bother it as long as you don't accidentally overwrite an important built-in Three.js key. For that reason I'd recommend that you put all of your custom keys in a "namespace" on the object so they're nice and neat and contained.
For example, if you had a Three.js object foo, you could put all your keys under foo.myCustomNamespace, so that your custom data like foo.myCustomNamespace.name, foo.myCustomNamespace.description, etc. are all together and won't interfere with THREE.js properties.
Edit: Three.js provides a built-in namespace for user data called, conveniently, userData. Access it on THREE.Object3D.userData.
https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L92
Something that might do something like
<img class="image" ... />
$(".image").get_colors()
I know there are few websites where you can upload your image and it would generate the color for you but I want something to put on my website
Something like this where you see the colors generated from the screenshot and can search by colors. I tried to check the source code but I could not see any reference to a js library.
I need this feature to work with js if possible.
EDIT:
The image would be on the page already; I just need to generate its color, so I don't want the uploading features.
Thanks.
You might be interested in this related question and my answer to another one.
Getting all the colors from an image is simple, at least in a browser that supports the canvas element - the technique is described here. You end up with a CanvasPixelArray (described here), which is essentially an array like [r,g,b,a,r,g,b,a, ...] where r,g,b,a are bytes indicating the red, green, blue, and alpha values of each pixel.
The hard part is identifying or creating a small selection of representative colors, rather than the 10,000 colors that might be in a 100x100 image. This is a pretty complicated problem, with many solutions (overview here). You can see Javascript implementations of two possible algorithms in the clusterfck library and my port of the Leptonica Modified Median Cut algorithm.
I did write just for fun. It is a jquery plugin, if you don't use it you can read it for some pointers. If there is some error during the call to get_colors a array is set in the return value to hold the errors, it returns an array of objects, these objects are a histogram of a image(one item in the array for every selected element).
(function($, window, document, undefined){
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext){
$.fn.get_colors = function(){
var rv = [];
this.each(function(){
var tagname = this.tagName.toLowerCase();
if ((tagname === 'img') || (tagname === 'canvas') || (tagname === 'video')){
//something bad can happend when drawing the image
try{
var w = this.getAttribute('width');
var h = this.getAttribute('height');
canvas.setAttribute('width', w);
canvas.setAttribute('height', h);
var ctxt = canvas.getContext('2d');
if (ctxt){
ctxt.drawImage(this, 0, 0);
var imagedata = ctxt.getImageData(0, 0, w, h);
var data = imagedata.data;
//log('imagedata.width:'+imagedata.width+' imagedata.height:'+imagedata.height+' w:'+w+' h:'+h);
var obj = {};
var color = '';
var r = 0, g = 0, b = 0, a = 0;
var pix = data.length;
for (pix--; pix > 2; pix-=4){
//a = data[pix - 0];
b = data[pix - 1];
g = data[pix - 2];
r = data[pix - 3];
if (r < 16) r = '0' + r.toString(16);
else r = r.toString(16);
if (g < 16) g = '0' + g.toString(16);
else g = g.toString(16);
if (b < 16) b = '0' + b.toString(16);
else b = b.toString(16);
//if (a < 16) a = '0' + r.toString(16);
//else a = a.toString(16);
//color = r + g + b + a;
color = r + g + b;
if (obj[color] > 0) ++obj[color];
else obj[color] = 1;
}
rv.push(obj);
imagedata = data = obj = null;
}
ctxt = null;
} catch(error){
if (!rv.errors){
rv.errors = [];
}
rv.errors.push(error);
}
}
});
return rv;
};
} else{
$.fn.get_colors = function(){
throw new Error('canvas element support required!');
};
}
})(jQuery, this, this.document);
If a document with only one image with 4 pixels(2x2) "#ff0000, #00ff00, #0000ff, #ff0000", if you do $('img').get_colors(); it returns [{"ff0000": 2, "0000ff": 1, "00ff00":1}].
To learn how to use the canvas element you could look at MDN and at the specs in development for details about pixel manipulation.
Edit: commented out a line I was using when debugging.
Have you seen this project on Github?
http://lokeshdhakar.com/projects/color-thief/
It's a javascript solution. (It depends on two additional libraries: jquery, quantize.js).
var colorThief = new ColorThief();
colorThief.getPalette(sourceImage, 8);
getPalette(sourceImage[, colorCount, quality])
Which will return an array, like so: [ [num, num, num], [num, num, num], ... ]