I m trying to understand how i can positioning my cubes in the canvas.
But i don't understand how positioning work.
I m looking a way to detect if my mesh meet the limit of the canvas. But what is the unit of position.x or position.y ?
And what is the relation between the canvas width , height and meshs on in the canvas?
Thanks.
var geometry = new Array(),material = new Array(),cube = new Array();
var i = 0;
for(i=0; i < 10;i++){
geometry[i] = new THREE.BoxGeometry(1,1,1);
material[i] = new THREE.MeshBasicMaterial({ color: 0x33FF99 });
cube[i] = new THREE.Mesh(geometry[i], material[i]);
scene.add(cube[i]);
}
camera.position.z = 5;
var render = function () {
requestAnimationFrame(render);
var xRandom = 0;
var yRandom = 0;
var zRandom = 0;
var sens = 1;
for (i = 0; i < cube.length ; i++) {
document.getElementById('widthHeight').innerHTML = " " + window.innerHeight + " " + window.innerWidth + "<br> x:" + cube[i].position.x + " <br> y:" + cube[i].position.y + " <br> z:" + cube[i].position.z;
xRandom = (Math.random() * 0.010 + 0.001) * sens;
yRandom = (Math.random() * 0.010 + 0.001) * sens;
zRandom = (Math.random() * 0.010 + 0.001) * sens;
cube[i].position.setX(xRandom + cube[i].position.x);
cube[i].position.setY(yRandom + cube[i].position.y);
cube[i].position.setZ(zRandom + cube[i].position.z);
cube[i].rotation.x += 0.0191;
cube[i].rotation.y += 0.0198;
}
renderer.render(scene, camera);
};
render();
i added a PlaneGeometry and some tests to detect if cubes reach limit x or y of the new PlaneGeometry.
var geometry = new Array(),material = new Array(),cube = new Array();
var i = 0;
var planeMap = new THREE.PlaneGeometry(100, 100);
var materialMap = new THREE.MeshBasicMaterial({ color: 0xCE0F0F });
var map = new THREE.Mesh(planeMap,materialMap);
scene.add(map);
for(i=0; i < 5; i++){
geometry[i] = new THREE.BoxGeometry(3,3,3);
material[i] = new THREE.MeshBasicMaterial({ color: 0x336699 });
cube[i] = new THREE.Mesh(geometry[i], material[i]);
scene.add(cube[i]);
}
camera.position.z = 100;
var render = function () {
requestAnimationFrame(render);
var xRandom = 0,yRandom = 0,zRandom = 0,x=0,y=0,z=0;
var sensX = 1, sensY = 1, sensZ = 1;
var widthHeight = document.getElementById('widthHeight');
for (i = 0; i < cube.length ; i++) {
if (cube[i].geometry.type == "BoxGeometry") {
widthHeight.innerHTML = " " + window.innerHeight + " " + window.innerWidth + "<br> x:" + cube[i].position.x + " <br> y:" + cube[i].position.y + " <br> z:" + cube[i].position.z;
var currentCube = cube[i].position;
var widthCube = cube[i].geometry.parameters.width;
var heightCube = cube[i].geometry.parameters.height;
x = currentCube.x;
y = currentCube.y;
z = currentCube.z;
if (x >= ((map.geometry.parameters.width / 2) - widthCube)) {
sensX = -1;
}
if (x <= ((map.geometry.parameters.width / 2) - widthCube)*-1) {
sensX = 1;
}
if (y >= ((map.geometry.parameters.height / 2) - heightCube)) {
sensY = -1;
}
if (y <= ((map.geometry.parameters.height / 2) - heightCube)*-1) {
sensY = 1;
}
xRandom = (Math.random() * 0.650 + 0.001) * sensX * (i + 1);
yRandom = (Math.random() * 0.850 + 0.001) * sensY * (i + 1);
//zRandom = (Math.random() * 0.450 + 0.001) * sensZ * (i + 1);
cube[i].position.setX(xRandom + x);
cube[i].position.setY(yRandom + y);
cube[i].position.setZ(zRandom + z);
cube[i].rotation.x += 0.01;
cube[i].rotation.y += 0.01;
}
}
In three.js you are working with a 3D space, the scene. The x,y,z coordinates are in that space. Similar to the real world, you can think of it as a room with some objects in it.
What gets shown on the canvas in 2D is how your camera sees a particular view to that scene. You can observe this by modifying the z coordinate of the camera position which you have in your code. This is again similar to taking a photo with a real camera of a room with some objects.
So the relation of the 3D object position and where they show in the canvas depend on the camera view. Three.js can tell you where some 3D position is projected in the 2D view -- for details and an example, see the answer there: Converting 3D position to 2d screen position [r69!]
Related
I need to build canvas animation like design requires. I spend almost 3 days but I'm not able to do anything like in design. Here a REQUESTED design!. And here - what I've got for now: current implementation which definitely not what requested from design .I need only animation of planet from particles at background (also whole process of animation changes in time, it starts from few particles but then amount growing and movings directions of particles changes)
here my current code:
export class CanvasComponent implements OnInit {
sphereRad = 280;
radius_sp = 1;
distance = 600;
particle_size = 0.7;
constructor() { }
ngOnInit() {
this.canvasApp();
}
canvasApp () {
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let displayWidth;
let displayHeight;
let wait;
let count;
let numToAddEachFrame;
let particleList;
let recycleBin;
let particleAlpha;
let r, g, b;
let fLen;
let m;
let projCenterX;
let projCenterY;
let zMax;
let turnAngle;
let turnSpeed;
let sphereCenterX, sphereCenterY, sphereCenterZ;
let particleRad;
let zeroAlphaDepth;
let randAccelX, randAccelY, randAccelZ;
let gravity;
let rgbString;
// we are defining a lot of letiables used in the screen update functions globally so that they don't have to be redefined every frame.
let p;
let outsideTest;
let nextParticle;
let sinAngle;
let cosAngle;
let rotX, rotZ;
let depthAlphaFactor;
let i;
let theta, phi;
let x0, y0, z0;
// INITIALLI
const init = () => {
wait = 1;
count = wait - 1;
numToAddEachFrame = 30;
// particle color
r = 255;
g = 255;
b = 255;
rgbString = 'rgba(' + r + ',' + g + ',' + b + ','; // partial string for color which will be completed by appending alpha value.
particleAlpha = 1; // maximum alpha
displayWidth = canvas.width;
displayHeight = canvas.height;
fLen = this.distance; // represents the distance from the viewer to z=0 depth.
// projection center coordinates sets location of origin
projCenterX = displayWidth / 2;
projCenterY = displayHeight / 2;
// we will not draw coordinates if they have too large of a z-coordinate (which means they are very close to the observer).
zMax = fLen - 2;
particleList = {};
recycleBin = {};
// random acceleration factors - causes some random motion
randAccelX = 0.1;
randAccelY = 0.1;
randAccelZ = 0.1;
gravity = -0; // try changing to a positive number (not too large, for example 0.3), or negative for floating upwards.
particleRad = this.particle_size;
sphereCenterX = 0;
sphereCenterY = 0;
sphereCenterZ = -3 - this.sphereRad;
// alpha values will lessen as particles move further back, causing depth-based darkening:
zeroAlphaDepth = 0;
turnSpeed = 2 * Math.PI / 1200; // the sphere will rotate at this speed (one complete rotation every 1600 frames).
turnAngle = 0; // initial angle
// timer = setInterval(onTimer, 10 / 24);
onTimer();
}
const onTimer = () => {
// if enough time has elapsed, we will add new particles.
count++;
if (count >= wait) {
count = 0;
for (i = 0; i < numToAddEachFrame; i++) {
theta = Math.random() * 2 * Math.PI;
phi = Math.acos(Math.random() * 2 - 1);
x0 = this.sphereRad * Math.sin(phi) * Math.cos(theta);
y0 = this.sphereRad * Math.sin(phi) * Math.sin(theta);
z0 = this.sphereRad * Math.cos(phi);
// We use the addParticle function to add a new particle. The parameters set the position and velocity components.
// Note that the velocity parameters will cause the particle to initially fly outwards away from the sphere center (after
// it becomes unstuck).
const p = addParticle(x0, sphereCenterY + y0, sphereCenterZ + z0, 0.002 * x0, 0.002 * y0, 0.002 * z0);
// we set some 'envelope' parameters which will control the evolving alpha of the particles.
p.attack = 50;
p.hold = 50;
p.decay = 100;
p.initValue = 0;
p.holdValue = particleAlpha;
p.lastValue = 0;
// the particle will be stuck in one place until this time has elapsed:
p.stuckTime = 90 + Math.random() * 20;
p.accelX = 0;
p.accelY = gravity;
p.accelZ = 0;
}
}
// update viewing angle
turnAngle = (turnAngle + turnSpeed) % (2 * Math.PI);
sinAngle = Math.sin(turnAngle);
cosAngle = Math.cos(turnAngle);
// background fill
context.fillStyle = '#000000';
context.fillRect(0, 0, displayWidth, displayHeight);
// update and draw particles
p = particleList.first;
while (p != null) {
// before list is altered record next particle
nextParticle = p.next;
// update age
p.age++;
// if the particle is past its 'stuck' time, it will begin to move.
if (p.age > p.stuckTime) {
p.velX += p.accelX + randAccelX * (Math.random() * 2 - 1);
p.velY += p.accelY + randAccelY * (Math.random() * 2 - 1);
p.velZ += p.accelZ + randAccelZ * (Math.random() * 2 - 1);
p.x += p.velX;
p.y += p.velY;
p.z += p.velZ;
}
/*
We are doing two things here to calculate display coordinates.
The whole display is being rotated around a vertical axis, so we first calculate rotated coordinates for
x and z (but the y coordinate will not change).
Then, we take the new coordinates (rotX, y, rotZ), and project these onto the 2D view plane.
*/
rotX = cosAngle * p.x + sinAngle * (p.z - sphereCenterZ);
rotZ = -sinAngle * p.x + cosAngle * (p.z - sphereCenterZ) + sphereCenterZ;
// m = this.radius_sp * fLen / (fLen - rotZ);
m = this.radius_sp;
p.projX = rotX * m + projCenterX;
p.projY = p.y * m + projCenterY;
p.projZ = rotZ * m + projCenterX;
// update alpha according to envelope parameters.
if (p.age < p.attack + p.hold + p.decay) {
if (p.age < p.attack) {
p.alpha = (p.holdValue - p.initValue) / p.attack * p.age + p.initValue;
} else if (p.age < p.attack + p.hold) {
p.alpha = p.holdValue;
} else if (p.age < p.attack + p.hold + p.decay) {
p.alpha = (p.lastValue - p.holdValue) / p.decay * (p.age - p.attack - p.hold) + p.holdValue;
}
} else {
p.dead = true;
}
// see if the particle is still within the viewable range.
if ((p.projX > displayWidth) || (p.projX < 0) || (p.projY < 0) || (p.projY > displayHeight) || (rotZ > zMax)) {
outsideTest = true;
} else {
outsideTest = false;
}
if (outsideTest || p.dead ||
(p.projX > displayWidth / (2 + (1 - Math.random())) && p.projZ + displayWidth * 0.1 > displayWidth / 2) ||
(p.projX < displayWidth / (2 - (1 - Math.random())) && p.projZ + displayWidth * 0.25 < displayWidth / 2)
) {
recycle(p);
} else {
// depth-dependent darkening
// console.log(turnAngle, rotZ)
depthAlphaFactor = 1;
// depthAlphaFactor = (1 - (1.5 + rotZ / 100));
depthAlphaFactor = (depthAlphaFactor > 1) ? 1 : ((depthAlphaFactor < 0) ? 0 : depthAlphaFactor);
context.fillStyle = rgbString + depthAlphaFactor * p.alpha + ')';
// draw
context.beginPath();
context.arc(p.projX, p.projY, m * particleRad, 0, 2 * Math.PI, false);
context.closePath();
context.fill();
}
p = nextParticle;
}
window.requestAnimationFrame(onTimer);
}
const addParticle = (x0, y0, z0, vx0, vy0, vz0) => {
let newParticle;
// const color;
// check recycle bin for available drop:
if (recycleBin.first != null) {
newParticle = recycleBin.first;
// remove from bin
if (newParticle.next != null) {
recycleBin.first = newParticle.next;
newParticle.next.prev = null;
} else {
recycleBin.first = null;
}
} else {
newParticle = {};
}
// if the recycle bin is empty, create a new particle (a new empty object):
// add to beginning of particle list
if (particleList.first == null) {
particleList.first = newParticle;
newParticle.prev = null;
newParticle.next = null;
} else {
newParticle.next = particleList.first;
particleList.first.prev = newParticle;
particleList.first = newParticle;
newParticle.prev = null;
}
// initialize
newParticle.x = x0;
newParticle.y = y0;
newParticle.z = z0;
newParticle.velX = vx0;
newParticle.velY = vy0;
newParticle.velZ = vz0;
newParticle.age = 0;
newParticle.dead = false;
if (Math.random() < 0.5) {
newParticle.right = true;
} else {
newParticle.right = false;
}
return newParticle;
}
const recycle = (p) => {
// remove from particleList
if (particleList.first === p) {
if (p.next != null) {
p.next.prev = null;
particleList.first = p.next;
} else {
particleList.first = null;
}
} else {
if (p.next == null) {
p.prev.next = null;
} else {
p.prev.next = p.next;
p.next.prev = p.prev;
}
}
// add to recycle bin
if (recycleBin.first == null) {
recycleBin.first = p;
p.prev = null;
p.next = null;
} else {
p.next = recycleBin.first;
recycleBin.first.prev = p;
recycleBin.first = p;
p.prev = null;
}
};
init();
}
}
So I will be happy with any help also REWARD(for full implementation) is possible (ETH, BTC any currency you wish).
I have a coin image coin.png I would like to appear and then fade up torwards the top of the canvas and then disappear but should be able to continuosly spawn randomly with the same behavior but im not sure how to do this with my current set up for example I am using my canvas in this manner
function writeMessage(canvas, message,x,y) {
var terminal = canvas.getContext('2d');
ClearCanvas();
terminal.font = "20px Comic Sans MS";
terminal.fillStyle = "rgb(0,255,1)";
terminal.textAlign = "center";
terminal.fillText(message, x, y);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById("terminalCanvas");
var terminal = canvas.getContext("2d");
terminal.fillStyle = "#000000";
terminal.fillRect(0,0,canvas.width,canvas.height);
terminal.font = "20px Comic Sans MS";
terminal.fillStyle = "rgb(0,255,1)";
terminal.textAlign = "center";
terminal.fillText("Coding Idle Terminal", canvas.width/2, canvas.height/2);
$('#terminalCanvas').click(function(evt){
WriteToCanvas();
function WriteToCanvas(){
if(Game.Terminal.HTMLSupport == 1 && Game.Terminal.CSSSupport == 0){
var rand = Math.floor(Math.random() * 122) + 1;
var tag = htmltags[rand];
Game.Player.money += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.exp += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.clicksTotal += Game.Player.clickIncrement + + (Game.Player.clickIncrement * Game.Player.codingGods/100);
var mousePos = getMousePos(canvas,evt);
var message = tag;
writeMessage(canvas, message,mousePos.x,mousePos.y);
}else if(Game.Terminal.CSSSupport == 1 && Game.Terminal.JavascriptSupport == 0){
var tagList = htmltags.concat(csstags);
var tagListLength = tagList.length;
var rand = Math.floor(Math.random() * tagListLength) + 1;
var tagg = tagList[rand];
Game.Player.money += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.exp += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.clicksTotal += Game.Player.clickIncrement + + (Game.Player.clickIncrement * Game.Player.codingGods/100);
var mousePos = getMousePos(canvas,evt);
var message = tagg;
writeMessage(canvas, message,mousePos.x,mousePos.y);
}else if(Game.Terminal.JavascriptSupport == 1 && Game.Terminal.PHPSupport == 0){
var t1 = csstags.concat(javascripttags);
var tagList = htmltags.concat(t1);
var tagListLength = tagList.length;
var rand = Math.floor(Math.random() * tagListLength) + 1;
var tagg = tagList[rand];
Game.Player.money += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.exp += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.clicksTotal += Game.Player.clickIncrement + + (Game.Player.clickIncrement * Game.Player.codingGods/100);
var mousePos = getMousePos(canvas,evt);
var message = tagg;
writeMessage(canvas, message,mousePos.x,mousePos.y);
}else if(Game.Terminal.PHPSupport == 1){
var t1 = csstags.concat(javascripttags);
var t2 = t1.concat(t1);
var tagList = htmltags.concat(t2);
var tagListLength = tagList.length;
var rand = Math.floor(Math.random() * tagListLength) + 1;
var tagg = tagList[rand];
Game.Player.money += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.exp += Game.Player.clickIncrement + (Game.Player.clickIncrement * Game.Player.codingGods/100);
Game.Player.clicksTotal += Game.Player.clickIncrement + + (Game.Player.clickIncrement * Game.Player.codingGods/100);
var mousePos = getMousePos(canvas,evt);
var message = tagg;
writeMessage(canvas, message,mousePos.x,mousePos.y);
}
}
});
function ClearCanvas(){
terminal.clearRect(0,0,canvas.width,canvas.height);
terminal.fillStyle = "#000000";
terminal.fillRect(0,0,canvas.width,canvas.height);
}
Now I would much prefer to have a seperate function I can call perhaps
function coinRandom(){
var number = Math.floor(Math.random() * 100) + 1;
if(number == 56){
//Draw coin to screen and other stuff
Game.Player.relics += 1;
}else{
//Do nothing
}
}
The biggest part of the function is it should get the mousePos.x and mousePos.y and draw the coin there and then from there fade out torwards the top of the canvas
You will need to establish a list of coins that are moving on the screen. The coins will need to be animated during each frame of the game. The function you desire will then add coins to the list at the appropriate time. You will need a second function that is called during each frame of the game that will check the list of coins, then animate and draw a coin for each entry in the list.
Here is a fiddle that uses random numbers to put the coin on the screen:
https://jsfiddle.net/05t6v8sL/
var rW = 400;
var rH = 500;
var coinImage = getCoinImage();
var coinsOnScreen = [];
var maxCoins = 50;
var risingSpeed = 200; //pixels per second...
var lastAnimationTime = 0;
function doDraw() {
var can = document.getElementById("myCanvas");
can.width = rW;
can.height = rH;
var context = can.getContext("2d");
//Erase the canvas
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, rW, rH);
//if there are less than maxCoins on the screen, add a new coin to a random position:
if (coinsOnScreen.length<maxCoins) {
//generate random x and y coordinates:
var newX = Math.floor(Math.random() * rW) + 1;
var newY = Math.floor(Math.random() * rH) + 1;
var newCoin = {
x: newX,
y: newY,
stl: 4
};
coinsOnScreen.push(newCoin);
}
//Now draw the coins
if (lastAnimationTime != 0) {
var deltaTime = new Date().getTime() - lastAnimationTime;
var coinRisePixels = Math.floor((deltaTime * risingSpeed)/1000);
var survivingCoins = [];
for (var i=0;i<coinsOnScreen.length;i++) {
var coin = coinsOnScreen[i];
coin.y = coin.y - coinRisePixels;
//the stl variable controlls the alpha of the image
coin.stl = (coin.stl * 1000 - deltaTime)/1000;
if (coin.y+50 > 0) {
var alpha = coin.stl/6;
context.save();
context.globalAlpha=alpha;
context.drawImage(coinImage, coin.x, coin.y);
context.restore();
//this coin is still on the screen, so promote it to the new array...
survivingCoins.push(coin);
}
}
coinsOnScreen = survivingCoins;
}
lastAnimationTime = new Date().getTime();
//Wait, and then call this function again to animate:
setTimeout(function() {
doDraw();
}, 30);
}
doDraw();
function getCoinImage() {
var image = new Image(50, 50);
image.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAOWUlEQVR42mL8sYUBJ2BkhGAGKGZkQKUZYPz/QPo/gzwDE4MJEBswMDOoA7EUEAsD5bnAav8xfAfi9wx/GJ4x/GW4BWRfAuJTQLn7/xmBrP8Ie/9DzENh/4diXAAggFgYKAH/GCSBDg/4z84QyMjBYMbAysAPdDzEEf+g9H+Eh8GYCUqD5H8zfGX4wXCW4SfDBiB/HVDuIblOAQggRnJiBGipGiMLQzojF0Pkfy6gZ0DCv4HET7DjII7/C6T+IUKREeoJRmaoGaxAzAbFIP53hrf/vzKsBsbYDGAMXSQ1RgACiDSP/GfgBjqmlJGXIZeBk0EIaCkDwzeg+C+g34Ae+APEv75B6H9/MD3CCPQIEzANsAA9wcoJpIGeYAay/4M8xQX11A+GL/8/MUwBBkQ30M53xHoEIICI98h/BmdgDLQy8jCYg0Kb4QtQCOiB3z+AdgPZf35B1IEcx8oOpNkhjgQ5HhyyQE/9/Q1R9+cnhAaJgdSzcwP9APQYI8hDPJDY+v+F4Qow4dUC3b4BFjP4PAIQQAQ9AkoQQMc0MAowVDKwAOEnoMgvSMh/fQ8xnEuQgYGDDxLS4DzCgJY/GNDyCSR/gT32A2jetw8QT4HNgcUMHzQm3oNjpwzI/I7PIwABhN8jIGexMMxmEmJIBIU+w2dIEvr8FugIIJ9HFGi5ADTd/0PS+J+Y3AmlmSCe+PERaO4bSAzyikBiExQ7wEKE4d97YKz8YogBGvsVl3EAAYTbI/8ZWIAOnMUoAvQEUDsjMAZ+AZPR59cMDJzA0OIVg6T3/38ZqAJAgQHy0JfXkBjiFgImOVDsAD0CzJMM/94xbPr/myEKGABYPQMQQIw/NmP1BCgupjAJM2SDPAEsUcD5AGQBvwTQI/xAg/8RF/LwOoEJEQn4YokJqO4HMOY/PgP6gRdiFwM71DPvGVYCS8VYoLrf6FoBAogFa7pjZGhgFmTI/vcFUip9BabjP0DPCMlBMvLfP0TXMwzMPJBU9xWYHDk5QBkOn68hAQTK/EIKQM88hdjFLQgJECYBhvC/7xjeAvNMNnqoAAQQ03+kog2M/zH4MvIx1P0DegAUG99BngCyBWUg6RZUrIJdRgT+D80DTMBk8+s3NAD+QZLQPzwYpA6kR0AGwv76AVwsM4AClomPIQtocBKsaIdhgABiQgtBXmAR2wWkGYHFH8NvYDH5E+gJAWlIJvwHzA///hPAyI76D0mWoFgFV/YgR/5DSpZ4MCzvCUpBiuqf0IAF5hMGYBXQDHSjBLJ6gABignP+gUOvAFj0afz7BLEMVLzyiEAzNT7L/yFCGpzOgTHHBExGLNzQRhAQCwBLOFYuCBsUgqCQ/vsXLTVgpg5wjPKKQvInSP2/z+D8C2rH1SI3gwACiPHrejhHGZihTgNra0FQNH4FFofMLJAilpiSCZTsmHghxSUDcgvuC5TmQRIDegKUdH99hCRVFmYG3CXBf0iJ9u0dUD0wdnkEoZmfneHnv48MNkB9Z0B6AQKIBVaqABWXAH0v+O8HpOYF1Re8UE/grIj+QypNVmBxzCgEaV+BkhGowoSXftBYArfDYBUiG8TToFLp10ugFDCU2djw+AVoLqcAxCOgJMb2H2wGO7AFUA1MaoEgNQABBE5awCiUAQpH/P8OsfjnF0hNDXIkrvQMi34WUCwALWEAhi7DG2imBFr8EZgU3oNqfqjrPgGT6/dvUI99g6oFOQpYnDMDmyc/f0JLLRz5DmQXqCgGeQQs9g0cM15At+uD9AEEEBM0HYYAg0PgH9Cw39DQZOOCOOg/DsNB+sB5AZRkoKUKqHnyA6j/yVNIkY2cYr4AA+cj0GNfPkM9B8JfIa0FThFIZP7+gz/zg4p+UGn2+zu8lc0GtCQS5BaAAAIVv4xATiC4CfIPogjkCVhDD2fRCqojOKDJ6A+0qQG08C2wvuACelAIGKPsrEgVFtABPKA66AekNIT3TX5AOmag1vAvaBfg/z8c+D+kjgE1VMFF+E9wfvQHstkBAogF6ChVoEfMQIIwDawckNggVGODamHktA/KuExA/dxckOTAyISqHhSTrCwQj7BxIEXXL0hKQC7FcOUVFmj/BaSW6Tc406sDlZsABBALMJqtgPHKAVIEao2CSghQ3vhPoPkBlv6LWtowAi1hATr0zx9IDc74D03PP0jy+wd09CdgcuTghqj78hGS7IR4oIHJgL9FDipNQYUROKCAdR4Q2gAEEAtQkz4sqYDKdpAirE1wLB4BqQeHELR0YgLGBK8wMA8DkxcLrDeI1FxhgHoe5HhQA/QdMH/8hdolCNTLzASNSXwWQztnoEoS3CmDNJc0AQKIBahLDVxPQDMUqHPz7z/hGAFZCEoioA4U3NNAPgewFGIBJptf75H67YzQGh2WxEB9J2YIhoUyrOYn1AtghCZZeMyBYoaFQR4ggEDhL/v/N6JIZWRCHcXA15348RXiEVY2qOBvSDHMwg/B4PzzA1IYcHND8sJfqOf+MyB6n///E9/cB+dNRtSWBKi5AhBALECOKHLdwMhAfB+DCaj+EzAZ8QpBM+9/aCn2BtrLg9XyQDa/GMRj4AoXmKR+f4OUPgywQoMEAHYuI8Qj0NgRBAggFqCjOeE1MCwiSAghUCn1Hlg7swEzKg+wcmRlhYbSb6QanglaRIPqAVD/HBhbbKDCBdTNfQXpNrMwk+gT5L4OsLACCCCQRxhREuZ/0kIHFM3soGY60FGvQe0zoGM5gY7l5II4jpERKaZ+whpmEE8xAz3EC6xvvj6CFBBsLAxE9L6gTSOYR6ARABBALEDB3xhFJCNpngGpZ2OFtIFA9cEXYKX6AcjmBjpSWBRROIBHZZiRminfII1JblloU/0jpM0FS+L48glaPv4DEECgRiOwXckgDC+N/pPgD+RxKyaIQ0GxwAJt9jMjjTI+ewJRzwOMBT4BaL74D20dA2k+YJ/n5Wdop4oJdSwDV9KCJy9gEQMQQKAmyjOYD8HF4F/CnR5YiQEqZrmAQcApCEku8Jr5H2aogtjMQEd+Bwbb66cQtQxIbS5QQLACk+SvX0R0uv6hxgwQvwYIIFDr9x5yBYiznYOEwf1qYJLgUwZ6QgXoGVVgqQTsY/+GVqrIbSPkUASFNAcrJK+AOkooPv0FGRf7958I+/8i5T0IfgIQQKCkdQU9j/xnIpzZ2HmhpdFrCA0aZOAAJplvQD4HG2abCRZToEBggrbLUAa4oQHwF9YXJzQyw4Bi/m2AAAI1448D8X/06MIXteC+NygJfIIWq78gyYNPDBIIf/9iNnNgxTrMQ8xs0IFsJM+Ami3wzEEoaTGiJK2TAAEECtNzQHwL2cb/fwlH7/fPaMOgoLYPsMgVkIKkc/AgNlrSAiUJWPuMSwQao1AzQHaCGpKgZgt4kAPPKAuaR18D8WGAAAJl9p9AvBU5KfwnMFICsvg7sKj89hltrBfUp5aBjEmB2mz//6ElWQZIi5dfETysg+gCAM14+wbSVwGVev8JFDQofZb/DLuB+C1AAMEG6FYBcT7MWYxEVIygwHz/DFIvcPJAm/Q/IBJcQI9wikHGoWAOEJGCdJ5YoSUcw3uoHLCo/gKMiZePgS1gTsLNeAyn/WdYDqIAAgg2HHQKiA9gFG94khZ4vgTo+DcPgUniHdLkDcgzb6H9BlHovAfQgVzAWGAVheapd/BhWYb3wMLh0W1g/mJDNONx2smAMSwFmr7bDYolgACCxQiI7ABiJ1iEMP4nUNND6x1moPw7YBPj42vI0CYvP8Q/4IruC6IZD/IcmP0H0jcHDUZ8AOr5DSwkeNkhPcd//wgM3mN2LzphDR+AAGJ8Og1FYg0QB8M0gjowf/8Q0aRnhKj7BSokmKGzUUDHcQGTnIAgxLD3QI98+QSZzQKNhDAC1bMDzWdnI7JNx4JUgEAC9ggQO0ATNQNAALGgObIGyHUDxgbvP2gTBDRqAe4CE9F/54A2O/58hRQGoLCCeeQtsJX7ByjGCfQgLwtkBOY/A3F5AjzmDCuxIBXhbyBVDvMECAAEEPog9g2gon5YqQHqW4NnBJgRY7mEMDjpM0NGUNiQRhxBXV+QJ1hYEI1IeO2Pp4RigpaK/36jFLtzgPqOIbsdIICYsBjQAZTYCatdQaMr4LzASuQIPBIGJ0vo4NbvX6gDe4TGfMFjVSyQmP77C6XoPQ3ENejuBgggFiwZDDT8FQvEO4HYEKzuJ6SfAUr3sF4dwSYyI2TE8tkdoJ6/kB4hBztq3YKzbGWEDEmBe88/UJIeqF0YAS33UABAAOFqVYFaUKEMkKQGDqU/0EExUEZmZEJNGrgwSO93YEX3C5jRuVmJiwVwK5gTIgb2BCLUn4LdBPIMFgAQQPiah3eBmv2ABt2EWQYyGNQBAlnExoGW1nGMDLIilUz/8SRDcD5ih5gNSkqgEU8UT/xnCIA2p7ACgABiIpA8bgMN8AUaeByWZ0CW/PwKYYPnx9mh/Rg0D8AwrLSBiSNPBsH6QKAxXVC3ABQbILP/Io3mA9VcBmI/8PQBHgAQQITHLyCecQfiNiD+CRu6BA0Y/ITOr4Im+0EOAdHgRQJoLV3k0okRWpzC9XBB8xPQrF9fEcUxUP1/IJ4K5LgA5c8RciZAADE+mog/4/1HarIA+XZAfheQZY5R1kPrBlBxyciE1ORGtgxpFcT/v4hZKwak1gO0v3QeSNcAOdsYocHNyIi/gAEIIFJXBx2CNmMSgbZlAS3Ugvf+YcM/jEhLP5jgqyfgIx7Ia0sYkZZKQX18D0jNBPJnQHs7RAOAACI1RhDjXsDaH0j5A1lRQLY9tHlIDvgJdPgxIL0CiNcCPfcWY20YETECEECUeASRbP4zaILaPUC+EZCtDmQD27oMvDhMBeWsh0BH3QTSF4BuOwBkX4LFCSO2RW5EeAQgwACQYpcXuHTdswAAAABJRU5ErkJggg==";
return image;
}
When each frame is drawn, the code will add a new coin to the coinsOnScreen array unless maxCoins count has been reached.
The number of pixels the coin will rise depends on the deltaTime of the frame rate. The pixels are subtracted from each coin in each frame until the coin is off the screen, at this point, the coin should be removed from the screen.
The alpha value of the coin is controlled by the "stl" variable that is assigned to each coin.
The effect you describe reminds me of when Mario grabs a coin and it briefly flies in the air. If this is more of the effect you desire, then instead of simply subtracting pixels from the y coordinate of the coin, you would calculate its path along a projectile function, like a parabola.
I am working on importing a model into a scene using the THREE.js OBJ loader.
I know that I am able to import the geometry fine, because when I assign a MeshNormalMaterial to it, it shows up great. However, if I use anything that requires UV coordinates, It gives me the error:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
I know this is because the loaded OBJ has no UV coordinates, but I was wondering if there was any way to generate the needed texture coordinates. I have tried
material.needsUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.buffersNeedUpdate = true;
...but to no avail.
Is there any way to automagically generate UV textures using three.js, or do I have to assign the coordinates myself?
To my knowledge there is no automatic way to calculate UV.
You must calculate yourself. Calculate a UV for a plane is quite easy, this site explains how: calculating texture coordinates
For a complex shape, I don't know how. Maybe you could detect planar surface.
EDIT
Here is a sample code for a planar surface (x, y, z) where z = 0:
geometry.computeBoundingBox();
var max = geometry.boundingBox.max,
min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for (var i = 0; i < faces.length ; i++) {
var v1 = geometry.vertices[faces[i].a],
v2 = geometry.vertices[faces[i].b],
v3 = geometry.vertices[faces[i].c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y),
new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y),
new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
]);
}
geometry.uvsNeedUpdate = true;
The other answers here were a great help but didn't quite fit my requirements to apply a repeating pattern texture to all sides of a shape with mostly flat surfaces. The problem is that using only the x and y components as u and v results in weird stretched textures on vertical surfaces.
My solution below uses surface normals to pick which two components (x, y and z) to map to u and v. It's still pretty crude but it works quite well.
function assignUVs(geometry) {
geometry.faceVertexUvs[0] = [];
geometry.faces.forEach(function(face) {
var components = ['x', 'y', 'z'].sort(function(a, b) {
return Math.abs(face.normal[a]) > Math.abs(face.normal[b]);
});
var v1 = geometry.vertices[face.a];
var v2 = geometry.vertices[face.b];
var v3 = geometry.vertices[face.c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2(v1[components[0]], v1[components[1]]),
new THREE.Vector2(v2[components[0]], v2[components[1]]),
new THREE.Vector2(v3[components[0]], v3[components[1]])
]);
});
geometry.uvsNeedUpdate = true;
}
This function doesn't normalise the UVs to the size of the object. This works better when applying the same texture to different sized objects in the same scene. However depending on the size of your world coordinate system, you'll probably need to scale and repeat the texture as well:
texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
Box UV mapping is possibly most useful thing in three.js configurators of any sort, -
https://jsfiddle.net/mmalex/pcjbysn1/
The solution works per face both with indexed and non-indexed buffer geometries.
Example of usage:
//build some mesh
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(new THREE.DodecahedronGeometry(2.5, 0));
let material = new THREE.MeshPhongMaterial({
color: 0x10f0f0,
map: new THREE.TextureLoader().load('http://mbnsay.com/rayys/images/1K_UV_checker.jpg')
});
//find out the dimensions, to let texture size 100% fit without stretching
bufferGeometry.computeBoundingBox();
let bboxSize = bufferGeometry.boundingBox.getSize();
let uvMapSize = Math.min(bboxSize.x, bboxSize.y, bboxSize.z);
//calculate UV coordinates, if uv attribute is not present, it will be added
applyBoxUV(bufferGeometry, new THREE.Matrix4().getInverse(cube.matrix), uvMapSize);
//let three.js know
bufferGeometry.attributes.uv.needsUpdate = true;
The example is based on the following implementation of applyBoxUV
function _applyBoxUV(geom, transformMatrix, bbox, bbox_max_size) {
let coords = [];
coords.length = 2 * geom.attributes.position.array.length / 3;
// geom.removeAttribute('uv');
if (geom.attributes.uv === undefined) {
geom.addAttribute('uv', new THREE.Float32BufferAttribute(coords, 2));
}
//maps 3 verts of 1 face on the better side of the cube
//side of the cube can be XY, XZ or YZ
let makeUVs = function(v0, v1, v2) {
//pre-rotate the model so that cube sides match world axis
v0.applyMatrix4(transformMatrix);
v1.applyMatrix4(transformMatrix);
v2.applyMatrix4(transformMatrix);
//get normal of the face, to know into which cube side it maps better
let n = new THREE.Vector3();
n.crossVectors(v1.clone().sub(v0), v1.clone().sub(v2)).normalize();
n.x = Math.abs(n.x);
n.y = Math.abs(n.y);
n.z = Math.abs(n.z);
let uv0 = new THREE.Vector2();
let uv1 = new THREE.Vector2();
let uv2 = new THREE.Vector2();
// xz mapping
if (n.y > n.x && n.y > n.z) {
uv0.x = (v0.x - bbox.min.x) / bbox_max_size;
uv0.y = (bbox.max.z - v0.z) / bbox_max_size;
uv1.x = (v1.x - bbox.min.x) / bbox_max_size;
uv1.y = (bbox.max.z - v1.z) / bbox_max_size;
uv2.x = (v2.x - bbox.min.x) / bbox_max_size;
uv2.y = (bbox.max.z - v2.z) / bbox_max_size;
} else
if (n.x > n.y && n.x > n.z) {
uv0.x = (v0.z - bbox.min.z) / bbox_max_size;
uv0.y = (v0.y - bbox.min.y) / bbox_max_size;
uv1.x = (v1.z - bbox.min.z) / bbox_max_size;
uv1.y = (v1.y - bbox.min.y) / bbox_max_size;
uv2.x = (v2.z - bbox.min.z) / bbox_max_size;
uv2.y = (v2.y - bbox.min.y) / bbox_max_size;
} else
if (n.z > n.y && n.z > n.x) {
uv0.x = (v0.x - bbox.min.x) / bbox_max_size;
uv0.y = (v0.y - bbox.min.y) / bbox_max_size;
uv1.x = (v1.x - bbox.min.x) / bbox_max_size;
uv1.y = (v1.y - bbox.min.y) / bbox_max_size;
uv2.x = (v2.x - bbox.min.x) / bbox_max_size;
uv2.y = (v2.y - bbox.min.y) / bbox_max_size;
}
return {
uv0: uv0,
uv1: uv1,
uv2: uv2
};
};
if (geom.index) { // is it indexed buffer geometry?
for (let vi = 0; vi < geom.index.array.length; vi += 3) {
let idx0 = geom.index.array[vi];
let idx1 = geom.index.array[vi + 1];
let idx2 = geom.index.array[vi + 2];
let vx0 = geom.attributes.position.array[3 * idx0];
let vy0 = geom.attributes.position.array[3 * idx0 + 1];
let vz0 = geom.attributes.position.array[3 * idx0 + 2];
let vx1 = geom.attributes.position.array[3 * idx1];
let vy1 = geom.attributes.position.array[3 * idx1 + 1];
let vz1 = geom.attributes.position.array[3 * idx1 + 2];
let vx2 = geom.attributes.position.array[3 * idx2];
let vy2 = geom.attributes.position.array[3 * idx2 + 1];
let vz2 = geom.attributes.position.array[3 * idx2 + 2];
let v0 = new THREE.Vector3(vx0, vy0, vz0);
let v1 = new THREE.Vector3(vx1, vy1, vz1);
let v2 = new THREE.Vector3(vx2, vy2, vz2);
let uvs = makeUVs(v0, v1, v2, coords);
coords[2 * idx0] = uvs.uv0.x;
coords[2 * idx0 + 1] = uvs.uv0.y;
coords[2 * idx1] = uvs.uv1.x;
coords[2 * idx1 + 1] = uvs.uv1.y;
coords[2 * idx2] = uvs.uv2.x;
coords[2 * idx2 + 1] = uvs.uv2.y;
}
} else {
for (let vi = 0; vi < geom.attributes.position.array.length; vi += 9) {
let vx0 = geom.attributes.position.array[vi];
let vy0 = geom.attributes.position.array[vi + 1];
let vz0 = geom.attributes.position.array[vi + 2];
let vx1 = geom.attributes.position.array[vi + 3];
let vy1 = geom.attributes.position.array[vi + 4];
let vz1 = geom.attributes.position.array[vi + 5];
let vx2 = geom.attributes.position.array[vi + 6];
let vy2 = geom.attributes.position.array[vi + 7];
let vz2 = geom.attributes.position.array[vi + 8];
let v0 = new THREE.Vector3(vx0, vy0, vz0);
let v1 = new THREE.Vector3(vx1, vy1, vz1);
let v2 = new THREE.Vector3(vx2, vy2, vz2);
let uvs = makeUVs(v0, v1, v2, coords);
let idx0 = vi / 3;
let idx1 = idx0 + 1;
let idx2 = idx0 + 2;
coords[2 * idx0] = uvs.uv0.x;
coords[2 * idx0 + 1] = uvs.uv0.y;
coords[2 * idx1] = uvs.uv1.x;
coords[2 * idx1 + 1] = uvs.uv1.y;
coords[2 * idx2] = uvs.uv2.x;
coords[2 * idx2 + 1] = uvs.uv2.y;
}
}
geom.attributes.uv.array = new Float32Array(coords);
}
function applyBoxUV(bufferGeometry, transformMatrix, boxSize) {
if (transformMatrix === undefined) {
transformMatrix = new THREE.Matrix4();
}
if (boxSize === undefined) {
let geom = bufferGeometry;
geom.computeBoundingBox();
let bbox = geom.boundingBox;
let bbox_size_x = bbox.max.x - bbox.min.x;
let bbox_size_z = bbox.max.z - bbox.min.z;
let bbox_size_y = bbox.max.y - bbox.min.y;
boxSize = Math.max(bbox_size_x, bbox_size_y, bbox_size_z);
}
let uvBbox = new THREE.Box3(new THREE.Vector3(-boxSize / 2, -boxSize / 2, -boxSize / 2), new THREE.Vector3(boxSize / 2, boxSize / 2, boxSize / 2));
_applyBoxUV(bufferGeometry, transformMatrix, uvBbox, boxSize);
}
The answers here are brilliant and helped me a lot.
Only one thing: If you are updating vertices, do not re-assign uvs, but set them, as in (scope is my geometry):
scope.updateUVs = (copy=true) => {
scope.computeBoundingBox();
var max = scope.boundingBox.max;
var min = scope.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
if (!copy) {
scope.faceVertexUvs[0] = [];
}
var faces = scope.faces;
for (i = 0; i < scope.faces.length ; i++) {
var v1 = scope.vertices[faces[i].a];
var v2 = scope.vertices[faces[i].b];
var v3 = scope.vertices[faces[i].c];
var uv0 = new THREE.Vector2( ( v1.x + offset.x ) / range.x , ( v1.y + offset.y ) / range.y );
var uv1 = new THREE.Vector2( ( v2.x + offset.x ) / range.x , ( v2.y + offset.y ) / range.y );
var uv2 = new THREE.Vector2( ( v3.x + offset.x ) / range.x , ( v3.y + offset.y ) / range.y );
if (copy) {
var uvs =scope.faceVertexUvs[0][i];
uvs[0].copy(uv0);
uvs[1].copy(uv1);
uvs[2].copy(uv2);
} else {
scope.faceVertexUvs[0].push([uv0, uv1, uv2]);
}
}
scope.uvsNeedUpdate = true;
}
This is a general version that works for spherical mapping (yaw, pitch coordinates), see example here, (look at loadSuzanne function):
function assignUVs(geometry) {
geometry.faceVertexUvs[0] = [];
geometry.faces.forEach(function(face) {
var uvs = [];
var ids = [ 'a', 'b', 'c'];
for( var i = 0; i < ids.length; i++ ) {
var vertex = geometry.vertices[ face[ ids[ i ] ] ].clone();
var n = vertex.normalize();
var yaw = .5 - Math.atan( n.z, - n.x ) / ( 2.0 * Math.PI );
var pitch = .5 - Math.asin( n.y ) / Math.PI;
var u = yaw,
v = pitch;
uvs.push( new THREE.Vector2( u, v ) );
}
geometry.faceVertexUvs[ 0 ].push( uvs );
});
geometry.uvsNeedUpdate = true;
}
I've tried everything and read every single link I can see on the internet regarding Perlin Noise or Simplex Noise and even dissected a few Javascript examples that I see work fine.
But I still get very random looking images... essentially just TV static.
My code is below. I'm using a random number generator so that I can seed a value, but I've tried with Math.random as well.
As near as I can tell, the different images generated at the different octaves aren't interpolating properly, or maybe the way I'm converting from the Noise function to RGB values is wrong (I've tried to fix both of these issues...).
if (!this.Prng) {
var Prng = function() {
var iMersenne = 2147483647;
var rnd = function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
that.seed = that.seed*16807%iMersenne;
return that.seed;
};
var that = {
seed: 123,
rnd: rnd,
random: function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
return rnd()/iMersenne;
}
};
return that;
}();
}
var CSimplexNoise = function(r)
{
this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],[1,0,1],[-1,0,1],
[1,0,-1],[-1,0,-1],[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
var p = [];
for(i = 0; i < 256; i++)
p[i] = Math.floor(r.random()*256);
this.perm = new Array();
for(i = 0; i < 512; i++)
{
this.perm[i] = p[i & 255];
}
}
CSimplexNoise.prototype.dot = function(g,x,y)
{
return g[0]*x + g[1]*y;
}
CSimplexNoise.prototype.GenerateSimplexNoise = function(x,y,octaves,persistence)
{
var total = 0;
for(i=0; i < octaves-1; i++)
{
var freq = Math.pow(2,i);
var amp = Math.pow(persistence,i);
total += this.InterpolatedNoise(x*freq,y*freq) * amp;
}
return total;
}
CSimplexNoise.prototype.InterpolatedNoise = function(x,y)
{
var xInt = Math.floor(x);
var xFrac = x - xInt;
var yInt = Math.floor(y);
var yFrac = y - yInt;
var v1 = this.SmoothNoise(xInt,yInt);
var v2 = this.SmoothNoise(xInt + 1,yInt)
var v3 = this.SmoothNoise(xInt,yInt+1)
var v4 = this.SmoothNoise(xInt + 1, yInt + 1);
var i1 = this.LinearInterpolate(v1,v2,xFrac);
var i2 = this.LinearInterpolate(v3,v4,xFrac);
return this.CosineInterpolate(i1,i2,yFrac);
}
CSimplexNoise.prototype.LinearInterpolate = function(a,b,x)
{
return a*(1-x) + b*x;
}
CSimplexNoise.prototype.CosineInterpolate = function(a,b,x)
{
var f = (1 - Math.cos(x*Math.PI)) * 0.5;
return a*(1-f) + b*f;
}
CSimplexNoise.prototype.SmoothNoise = function(x,y)
{
var corners = (this.Noise(x-1,y-1) + this.Noise(x+1,y-1) + this.Noise(x-1,y+1) + this.Noise(x+1,y+1)) / 16;
var sides = (this.Noise(x-1,y) + this.Noise(x+1,y) + this.Noise(x,y-1) + this.Noise(x+1,y+1)) / 8;
var center = this.Noise(x,y) / 4;
return corners + sides + center;
}
CSimplexNoise.prototype.Noise = function(xin, yin)
{
var n0, n1, n2;
var F2 = 0.5*(Math.sqrt(3)-1);
var s = (xin+yin)*F2;
var i = Math.floor(xin+s);
var j = Math.floor(yin+s);
var G2 = (3-Math.sqrt(3))/6;
var t = (i+j)*G2;
var X0 = i-t;
var Y0 = j-t;
var x0 = xin-X0;
var y0 = yin-Y0;
var i1,j1;
if(x0 > y0)
{
i1 = 1;
j1 = 0;
}
else
{
i1 = 0;
j1 = 1;
}
var x1 = x0 - i1 + G2;
var y1 = y0 - j1 + G2;
var x2 = x0 - 1 + 2 * G2;
var y2 = y0 - 1 + 2 * G2;
var ii = i & 255;
var jj = j & 255;
var gi0 = this.perm[ii + this.perm[jj]] % 12;
var gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12;
var gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12;
var t0 = 0.5 - x0 * x0 - y0 * y0;
if(t0 < 0)
n0 = 0;
else
{
t0 *= t0;
n0 = t0 * t0 * this.dot(this.grad3[gi0],x0,y0)
}
var t1 = 0.5 - x1 * x1 - y1 * y1;
if(t1 < 0)
n1 = 0;
else
{
t1 *= t1;
n1 = t1 * t1 * this.dot(this.grad3[gi1],x1,y1);
}
var t2 = 0.5 - x2 * x2 - y2 * y2;
if(t2 <0 )
n2 = 0;
else
{
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2],x2,y2);
}
return 70 * (n0 + n1 + n2);
}
$(document).ready(function(){
var context = $('#screen')[0].getContext("2d");
var w = 100;
var h = 100;
var data = context.createImageData(w,h);
var simplexNoise = new CSimplexNoise(Prng);
for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
// var newVal = ((simplexNoise.GenerateSimplexNoise(x,y,5,0.25) - -1) / (1 - -1)) * (255 - 0);
var newVal2 = simplexNoise.GenerateSimplexNoise(x,y,5,0.5)
var newVal = Math.floor(newVal2*256);
newVal = Math.abs(newVal * 2)-0.5;
data.data[((h * y) + x) * 4] = newVal;
data.data[((h * y) + x) * 4+1] = newVal;
data.data[((h * y) + x) * 4+2] = newVal;
data.data[((h * y) + x) * 4+3] = 255;
}
}
context.putImageData(data,0,0);
})
Try sampling simplexNoise.GenerateSimplexNoise(x * 0.05, y * 0.05, 5, 0.5)
The problem may be that your samples are too far apart. (this would result in apparently random behavior, since the simplex noise might go through more than half a wavelength before you sample it)
REVISION: Updated numbers above...
You may actually need to reduce the samples so that there are 20 in a given wavelength of the simplex noise. The average wavelength of most simplex noise is 1, so 0.05 should do the trick. Also, you may want to test with just one octave at first.
I have a problem with Raphael.js. I want to rotate the "compassScale" - set in the following code - in a relative manner.
This works for the paths, but all the texts "animate" to the absolute rotation of 30 degree. I want them to rotate to the 30 degrees relative from their actual positions.
var compassScale = paper.set();
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
compassScale.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
Like you said, the problem is that you're animating all elements to 30 degrees, not their current rotation + 30 degrees. It's actually quite simple once you think of it that way. Here is your revised code that works:
var compassScale = paper.set();
var texts = []; // array to hold the text elements
var centerX = 200;
var centerY = 200;
var radius = 195;
var compasCircle = paper.circle(centerX, centerY, radius);
for(var i = 0; i < 360; i++) {
var winkelRad = i * (Math.PI/180)
var xStart = centerX + Math.sin(winkelRad) * radius;
var yStart = centerY + Math.cos(winkelRad) * radius;
var diff = 6;
if(i % 10 === 0){
texts.push(paper.text(centerX, centerY - radius + 18, i).rotate(i, centerX, centerY, true));
diff = 12;
} else if(i % 5 === 0) {
diff = 8;
}
var xEnd = centerX + Math.sin(winkelRad) * (radius - diff);
var yEnd = centerY + Math.cos(winkelRad) * (radius - diff);
compassScale.push(paper.path("M" + xStart + " " + yStart + " L" + xEnd + " " + yEnd));
}
compassScale.animate({rotation:"30 " + centerX + " " + centerY}, 5000);
// loop through the text elements, adjusting their rotation by adding 30 to their individual rotation
for (var i = 0, l = texts.length; i < l; i += 1) {
// node.attr("rotation") returns something like 50 200 200, so we have to split the string and grab the first number with shift
texts[i].animate({rotation: (30 + +texts[i].attr("rotation").split(" ").shift()) + " " + centerX + " " + centerY}, 5000);
}
Just a quick observation:
Looks like "Rotation" isn't part of the Atrr anymore since ver 2, so you can't use it in "animate", but you can replace that with "transform: "r" + (some degree)"..
eg:
element.animate( {transform: "r" + (-90)}, 2000, 'bounce');