THREE JS applying Gradient to imported Models - javascript

The difference between the following two spheres - in terms of how their gradient colors were applied, comes down to one statement:
sphereGeometry = sphereGeometry.toNonIndexed();
Being that I really like the smoother look that .toNonIndexed() gives us, I tried applying it to some of the imported “.glb” models available on the THREE.js GIT - but it’s not working.
For example, here’s what happens when I use the horse model available here: https://github.com/mrdoob/three.js/blob/master/examples/models/gltf/Horse.glb
It basically completely ignore my colors and defaults to red and black for some reason.
But when I comment out the .toNonIndexed() line, it gives me the colors I asked for - except you definitely see the triangles, which is the look I'm trying to avoid:
Here's my code for loading the object:
function loadAny3DModel() {
loader.load("./Horse.glb", function(theHorse) {
console.log("===>'theHorse' has arrived!!!\n");
var horseScene = theHorse.scene;
horseMesh = horseScene.children[0];
var horseGeometry = horseMesh.geometry;
let horseMat = horseMesh.material;
var horseVertexPositionsArray = horseGeometry.attributes.position;
// Here's the command that isn't working:
// horseGeometry = horseGeometry.toNonIndexed();
// horseVertexPositionsArray = horseGeometry.attributes.position;
let theColor = new THREE.Color();
let colorsArray = [];
for(let i = 0; i < horseVertexPositionsArray.count; i++) {
let randC1 = "purple";
let randC2 = "white";
let chosenColor = i % 2 == 0 ? randC1 : randC2;
theColor.set(chosenColor);
colorsArray.push(theColor.r, theColor.g, theColor.b);
}
horseGeometry.setAttribute("color", new THREE.Float32BufferAttribute(colorsArray, 3));
horseMat.vertexColors = true;
render();
scene.add(horseScene);
}
}
What should I be doing to get the smoother gradients going?
=====================================================================
UPDATE:
Here is a very rough idea of what I'm trying to do: extend a gradient over an entire model, as opposed to every single triangle that is forming the model. (Compare this image to the one above.)

If you comment in the following line...
horseGeometry = horseGeometry.toNonIndexed();
...it means you create a new (!) geometry. As long as you don't assign the geometry back to Mesh.geometry, this code won't have any effect. So the fix is to add the following line after using toNonIndexed():
horseMesh.geometry = horseGeometry;

Related

Linking two.js with dat.GUI

I want to link the change letter.linewidth = 10 with a control in dat.GUI.
Here is the code for the full letter variable:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 10;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#333';
To add an element to dat.GUI it says in the docs "The property must be public, i.e. defined by this.prop = value", though when I add this. in front of letter.linewidth it breaks the functionality of two.js and does not interpret the SVG.
I'm kinda' new to JavaScript and having a tough time figuring this one out.
Any help would be greatly appreciated.
So after lots of playing around, I found the fix.
Here is the code to draw the SVG through two.js:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 100;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#272727';
letter.scale = 1;
I was calling the letter wrong with dat.GUI. Here is my code for dat.GUI:
window.onload = function() {
var gui = new dat.GUI();
gui.add(letter, 'linewidth', 1, 100);
}
I don't know if this will be useful for anyone, but hey, hopefully this will help if somebody is running into the same problem.

Switching id names in javascript

Working on chess board using html and javascript. I am trying to add a function so that the user can flip the perspective of the board and running into a glitch that I understand, but can't figure out how to solve. Code as below....
function flipBoard(){
document.getElementById("a8").id = "h1";
document.getElementById("a7").id = "h2";
document.getElementById("a6").id = "h3";
document.getElementById("a5").id = "h4";
document.getElementById("a4").id = "h5";
document.getElementById("a3").id = "h6";
document.getElementById("a2").id = "h7";
document.getElementById("a1").id = "h8";
document.getElementById("h8").id = "a1";
document.getElementById("h7").id = "a2";
document.getElementById("h6").id = "a3";
document.getElementById("h5").id = "a4";
document.getElementById("h4").id = "a5";
document.getElementById("h3").id = "a6";
document.getElementById("h2").id = "a7";
document.getElementById("h1").id = "a8";
}
So.... I thought this would work just fine, but discovered that this wreaks havoc on my board by position half white, half black pieces on both sides of the board. The problem of course is that after the original "a1" square is renamed to "h8", there are now TWO "h8" squares, and at the end of the code it switches both back to "a1".
I have no idea how to get the id names to switch at the same time, otherwise I'd have to add a whole lot of code switching the id names to some third name as a place holder before switching them over to the desired name. Possible, but tedious and I feel there has to be a simpler way to do this.
Any ideas?
You can reduce the repetition of your current code by generating the id values programmatically in a loop:
function flipBoard(){
var a, h, aVal, hVal, aEl, hEl;
for(a = 8, h = 1; a > 0; --a, ++h) {
aVal = 'a' + a;
hVal = 'h' + h;
// Store off the current values
aEl = document.getElementById(aVal);
hEl = document.getElementById(hVal);
// swap them
aEl.id = hVal;
hEl.id = aVal;
}
}
demo fiddle
Save the reference to the element in variable. For example:
function flip(){
var aEight = document.getElementById("a8"),
hOne = document.getElementById("h1");
aEight.id = "h1";
hOne.id = "a8";
}
By separating out the steps of finding your elements and changing their ids, you'll easily be able to keep track of the flipped elements.

How to add new line character for each node text in arbor js

I have a network,with 14 nodes, and each node has a label, and also edges for connecting these nodes to each other. I tried to enter a long label for one of these nodes, and unfortunatly it seems that arborjs show labels on nodes just horizontally, so I tried to put a new line character in a label text "\n" and it will render it as an space, so I was wondering if anybody knows how to have multiple line label for a node in arbor js?
Here is the code :
<script language="javascript" type="text/javascript">
var sys = arbor.ParticleSystem(1000, 700,0.01);
sys.parameters({gravity:false});
sys.renderer = Renderer("#viewport") ;
var data = {
nodes:{
STRUCTURE:{'color':'black','shape':'rect','label':'STRUCTURE', },
Engineering:{'color':'salmon','shape':'rect','label':'Engineering'},
Architecture:{'color':'salmon','shape':'rect','label':'Architecture'},
ArtsSciences:{'color':'salmon','shape':'rect','label':'Arts & Sciences'},
EarthEnergy:{'color':'salmon','shape':'rect','label':'Earth & Energy'},
SustainableDesign:s{'color':'lightskyblue','shape':'rect','label':'Sustainable Design'},
sutabledesignleaf1:{'color':'lawngreen','shape':'rect','label':'Earthen Structures'},
MaterialsStructures:{'color':'lightskyblue','shape':'rect','label':'Materials & Structures'},
MaterialsStructuresleaf1:{'color':'lawngreen','shape':'rect','label':'AEROSPACE/MECH. ENGINEERING'},
LithosphereDynamics:{'color':'lightskyblue','shape':'rect','label':'Lithosphere Dynamics'},
Energy:{'color':'lightskyblue','shape':'rect','label':'Energy'},
LithosphereDynamicsleaf1:{'color':'lawngreen','shape':'rect','label':'Structure/Tectonophy'},
Energyleaf1:{'color':'lawngreen','shape':'rect','label':'Structural Control on Reservoirs'},
ArtsSciencesleaf1:{'color':'lawngreen','shape':'rect','label':'VARIOUS THEMES Market Structure'},
},
edges:{
STRUCTURE:{ Engineering:{}, Architecture:{} , ArtsSciences:{}, EarthEnergy:{}},
ArtsSciences:{ArtsSciencesleaf1:{}},
EarthEnergy:{Energy:{},LithosphereDynamics:{}},
Energy:{Energyleaf1:{}},
LithosphereDynamics:{LithosphereDynamicsleaf1:{}},
Engineering:{MaterialsStructures:{}},
MaterialsStructures:{MaterialsStructuresleaf1:{}},
Architecture:{SustainableDesign:{}},
SustainableDesign:{sutabledesignleaf1:{}}
}
};
sys.graft(data);
var canvas = document.selectElementById('viewport') ;
var context = canvas.getContext('2d');
context.font = '40pt Calibri';
context.fillStyle = 'blue';
</script>
suppose in this code, for STRUCTURE node's label we have "STRUCTURE" but when I want to have like a long text lie "kaskdjhkjahdkjhaskjdhjkahskjdhakjshdkjahdkjhaskjdhkjahsdkjhakjsdhkjashdkjhasdkjhkajshdkjhakjdhkajshdk" it doesn't break it to two lines, it will show up as one line, and even if I put a new line character, it will consider it as a space and it will show that again in one line, any help is appreciated.
Thanks in advance
Thank you for your answer, last night, I figured out that unfortunately there is no way on canvas fillText() to draw text in multiple line, so I had to write my wrapper for that, here is the function that I wrote, and it's working right now,
function wrap_text(var rawstring,var line_width)
{
var strarray = new Array() ;
var temp_str = new Array() ;
var j = 0 ;var i = 0 ;
strarray = text.split(' ');
var temp = strarray[j] ;
j++;
while(j < strarray.length)
{
while(temp.length <30)
{
temp = temp+" "+strarray[j];
j++;
}
temp_str[i]=temp ; i++;
var temp = "" ;
}
return temp_str;
}
var wrap_result = wrap_text('kjakjhkjashd kajasd asdmbdmnad nmauhiqwe kbawem mnbasdm',20);
for (var i = 0; i <wrap_result.length ; i--)
{
console.log(wrap_result[i]);
}
I know this post is 2 year too late, though I was still having the same issue and unable to find the solution anywhere. The main problem is the canvas fillText does not support multiple lines. So you have to add multiple lines to the label. The trick is to split out the returns into an array and loop over the array for each new line.
To keep the code clean, I prefer to not to directly edit the function in the files. To get the multiple lines to work add to your custom JS the below function which will override the Cytoscape internal label rendering. Works for both cytoscape.js & cytoscape.min.js
//function to override the default label rending to support multiple lines
;(function($$){ 'use strict';
var CanvasRenderer = $$('renderer', 'canvas');
CanvasRenderer.prototype.drawText = function(context, element, textX, textY) {
var style = element._private.style;
var parentOpacity = element.effectiveOpacity();
if( parentOpacity === 0 ){ return; }
var text = this.setupTextStyle( context, element );
if ( text != null && !isNaN(textX) && !isNaN(textY) ) {
var lineWidth = 2 * style['text-outline-width'].value; // *2 b/c the stroke is drawn centred on the middle
if (lineWidth > 0) {
context.lineWidth = lineWidth;
context.strokeText(text, textX, textY);
}
//START NEW CODE
//remove old label rendinging
//context.fillText(text, textX, textY);
//explode the text into an array split up by line returns
var lines = text.split("\n");
//loop through each of the lines
for (var index = 0; index < lines.length; ++index) {
//render the multiple lines
context.fillText(lines[index], textX, (textY + (style['font-size'].pxValue * 1.2 * index)));
}
//END NEW CODE
}
};
})( cytoscape );
This question really concerns canvas.fillText(), not arbor.js, see this question. In short, this is a limitation of the fillText function.
That said, you can solve this problem relatively simply. The following all assumes you are basing your rendering on the "atlas" example, but you can figure it out from the idea anyway I think.
// draw the text
if (label){
ctx.font = "bold 11px Arial"
ctx.textAlign = "center"
// if (node.data.region) ctx.fillStyle = palette[node.data.region]
// else ctx.fillStyle = "#888888"
ctx.fillStyle = "#888888"
// ctx.fillText(label||"", pt.x, pt.y+4)
ctx.fillText(label||"", pt.x, pt.y+4)
}
Check out the code above to see how the text is rendered. We can replace the line ctx.fillText(label||"", pt.x, pt.y+4) with a loop based on multiple lines you want to render. Make your label value something like line 1\nline 2\nline 3, then use String.split() to make it into an array like so:
var lines = label.split("\n");
Now, loop through lines and render a ctx.fillText() for each one. If you are basing your project closely on the examples you may need to adjust the layout to make it look right, but hopefully this has put you on the right track.

Simple javascript game, hide / show random square

I'm working on a simple game and I need some help to improve my code
So here's the game:
Some square show and hide randomely for a few seconds and you have to clic on them.
I use RaphaelJS to draw the square and a few of JQuery ($.each() function)
I work in a div, that's how I draw my squares (6 squares), x y are random numbers.
var rec1 = paper.rect(x, y, 50, 50).attr({
fill: "blue",});
Can I use for() to build my squares with a different var name for each one ?
I try with var = varName+i but it didn't work.
To hide and show the square I use two functions call with two setTimeout:
function box1() {rec1.show();}
function hidebox1() {rec1.hide();}
var time1 = setTimeout(box1, 1000);
var time1 = setTimeout(hidebox1, 2000);
I know it looks crappy...
I'm sure there is a way to use a toggle, or something more fancy to do that if you could help me finding it :) Because right now I have to do that for every square...
Thanks a lot for your help.
Your instinct to try to use varName plus some i to identify which varName you want is spot on, and JavaScript (like most languages) has that idea built in through what's called an array.
A simple one looks something like this:
var foo = [1, 5, 198, 309];
With that array, you can access foo[0] which is 1, or foo[3] which is 309.
Note two things: First, we identify which element of the array we want using square brackets. Second, we start counting at 0, not 1.
You can create an empty array like var varName = []; and then add new elements to it using varName.push( newValueToPutIn );
With those tools, you can now get at what you wanted. Now you can do something like:
var recs = [];
for(var i = 0; i < 100; i++) {
var rec = paper.rect(x, y, 50, 50).attr({fill: 'blue'});
recs.push(rec);
}
And recs[0] and recs[1] and so forth will refer to your various boxes.
For the first question, an array is the way to go.
For the second part, you could encapsulate the square and its show/hide stuff into a new anonymous object, like this:
var recs = [];
var numberOfRecs = 6;
for (var i = 0; i < numberOfRecs; i++) {
//determine x and y?
recs.push({
box: paper.rect(x, y, 50, 50).attr({ fill: "blue" }),
showBriefly: function(timeFromNow, duration) {
window.setTimeout(this.box.show, timeFromNow);
window.setTimeout(this.box.hide, timeFromNow + duration);
}
});
}
//show the 3rd box 1000 ms from now, for a duration of 1000 ms
recs[2].showBriefly(1000, 1000);

Is There An Efficient jQuery Hittest?

I am creating a simple game with HTML, CSS, and JavaScript (jQuery). There is main ship, where all of the particles (bullets) originate from. They are each just divs. Then, enemy divs are places randomly throughout the screen.
I am looking for an efficient way to test if each particle is hitting a particular enemy. I have something that starts to work out fine, but gets bogged down incredibly fast. I am new to js, so my code is pretty messy and probably inefficient in many other ways. Any suggestions would be greatly appreciated!
Here is my section that creates enemies and tests for hitting:
var createEnemy = function(){
var xRandom = Math.floor(Math.random() * (containerW-50));
var yRandom = Math.floor(Math.random() * (containerH-50));
var newEnemy = $('<div class="enemy"></div>');
$(newEnemy).css({'left':xRandom,'top':yRandom}).appendTo('#container').fadeTo(200, .7);
var hitTest = setInterval(function(){
var enemy = $(newEnemy);
var particle = $('.particle');
var enemyT = enemy.offset().top;
var enemyB = enemy.height()+enemyT;
var enemyL = enemy.offset().left;
var enemyR = enemy.width()+enemyL;
var particleT = particle.offset().top;
var particleB = particle.height();
var particleL = particle.offset().left;
var particleR = particle.width();
if(particleT >= enemyT-particleB && particleT <= enemyB && particleL >= enemyL-particleR && particleL <= enemyR){
enemy.hide();
var removeEnemy = setTimeout(function(){
newEnemy.remove();
clearInterval(hitTest, 0);
},500);
}
}, 20);
}
var enemyInt = setInterval(createEnemy, 1000);
Is getting something like this to run smoothly in a browser realistic? Does my code just need some changes? You will probably need more context so:
EDIT 1/12/2012: Game Link Removed // Not Relevant
NOTE: This works best in Chrome and Safari at the moment.
EDIT 3/22/2011: Changed enemy fadeOut() to hide() so that you can see exactly when an enemy disappears (it is sometimes delayed). The hitTest only seems to trigger when you click on the actual enemy, so if it passes through, it is not being triggered.Also, I forgot to clearInterval on hitTest. This seemed to boost performance dramatically, but still isn't quite there.
If you want the best performance, drop jQuery and use native JavaScript.
If that isn't an option, profile the slowest parts and use native DOM there, e.g.
var newEnemy = $('<div class="enemy"></div>');
...becomes...
var newEnemy = document.createElement('div');
newEnemy.className = 'enemy';

Categories

Resources