Finding length of arc on unit circle only given x position - javascript
Some background:
I've been trying to map a texture onto a "sphere" using a look up table of texture co-ordinate changes. This is for a really slow micro controller to draw on a little LCD panel. So Three.JS is out, WebGL etc... the look up table should work!
The equations for texturing a sphere all pinch the poles. I can't "pre-spread" the texture of these extremes because the texture offset changes to make the "sphere" appear to rotate.
If you examine the code for making the lookup table here, you'll see the approach, and the running demo shows the issue.
https://codepen.io/SarahC/pen/KKoKqKW
I figured I'd try and come up with a new approach myself!
After thinking a while, I realised a sphere texture in effect moves the location of the texture pixel further from the spheres origin the further away from the origin it is! In a straight line from the origin.
So I figured - calculate the angle the current pixel is from the origin, calculate it's unit distance, then all I need to do is make a function that is given the distance, and calculates the new distance based on some "sphere calculation". That new distance is almost 1 to 1 near the center of the sphere, and rapidly increases right at the edges. Mapping a sphere texture!
That offset function I figured (may be wrong here?) (diagrammed below) given the distance from the origin L1 (unit circle) it returns the length of the arc L2 which in effect is the flat pixel offset to use from the source texture.
(I asked on Reddit, and got given Math.acos for X, but now I know that's wrong, because that's the X position of the circle! Not the straight line X position from the offset, AND it gives an angle, not the Y position... wrong on two counts. Oooph!
Oddly, surprisingly, because I thought it gave the Y position, I dropped it into an atan2 function, and it ALMOST worked... for all the wrong reasons of course but it made a bump at the center of the "sphere".
The current "state of the mistake" is right here:
https://codepen.io/SarahC/pen/abYbgwa?editors=0010 )
Now I know that aCos isn't the function I need, I'm at a loss for the solution.
But! Perhaps this approach I thought up is stupid? I'd happily use any look-up mapping function you think would work. =)
Thanks for your time and reading and sharing, I like learning new stuff.
//JS
An interesting but befuddling problem...
Per Spektre's comment and my follow up comment, the mapping of x to the length of the circle's arc still resulted in the center bubble effect of the image as described in the question. I tried numerous mathematically "correct" attempts, including picking a distant view point from the sphere and then calculating the compression of the 2d image as the view point angle swept from top center of the sphere to the edge, but again, to no avail, as the bubble effect persisted...
In the end, I introduced a double fudge factor. To eliminate the bubble effect, I took the 32nd root of the unit radius to stretch the sphere's central points. Then, when calculating the arc length (per the diagram in the question and the comments on "L2") I undid the stretching fudge factor by raising to the 128th power the unit radius to compress and accentuate the curvature towards the edge of the sphere.
Although this solution appears to provide the proper results, it offends the mathematician in me, as it is a pure fudge to eliminate the confusing bubble effect. The use of the 32nd root and 128th power were simply arrived at via trial and error, rather than any true mathematical reasoning. Ugh...
So, FWIW, the code snippet below exemplifies both the calculation and use of the lookup table in functions unitCircleLut2() and drawSphere2(), respectively...
// https://www.reddit.com/r/GraphicsProgramming/comments/vlnqjc/oldskool_textured_sphere_using_lut_and_texture_xy/
// Perhaps useable as a terminator eye?........
// https://www.youtube.com/watch?v=nSlEQumWLHE
// https://www.youtube.com/watch?v=hx_0Ge4hDpI
// This is an attempt to recreate the 3D eyeball that the Terminator upgrade produces on the Adafruit M4sk system.
// As the micro control unit only has 200Kb RAM stack and C and no 3D graphics support, chances are there's no textured 3D sphere, but a look up table to map an eyeball texture to a sphere shape on the display.
// I've got close - but this thing pinches the two poles - which I can't see happening with the M4sk version.
// Setup the display, and get its pixels so we can write to them later.
let c = document.createElement("canvas");
c.width = 300;
c.height = 300;
document.body.appendChild(c);
let ctx = c.getContext("2d");
let imageDataWrapper = ctx.getImageData(0, 0, c.width, c.height);
let imageData = imageDataWrapper.data; // 8 bit ARGB
let imageData32 = new Uint32Array(imageData.buffer); // 32 bit pixel
// Declare the look up table - dimensions same as display.
let offsetLUT = null;
// Texture to map to sphere.
let textureCanvas = null;
let textureCtx = null;
let textureDataWrapper = null;
let textureData = null;
let textureData32 = null;
let px = 0;
let py = 0;
let vx = 2;
let vy = 0.5;
// Load the texture and get its pixels.
let textureImage = new Image();
textureImage.crossOrigin = "anonymous";
textureImage.onload = _ => {
textureCanvas = document.createElement("canvas");
textureCtx = textureCanvas.getContext("2d");
offsetLUT = unitCircleLut2( 300 );
textureCanvas.width = textureImage.width;
textureCanvas.height = textureImage.height;
textureCtx.drawImage(textureImage, 0, 0);
textureDataWrapper = textureCtx.getImageData(0, 0, textureCanvas.width, textureCanvas.height);
textureData = textureDataWrapper.data;
textureData32 = new Uint32Array(textureData.buffer);
// Draw texture to display - just to show we got it.
// Won't appear if everything works, as it will be replaced with the sphere draw.
for(let i = 0; i < imageData32.length; i++) {
imageData32[i] = textureData32[i];
}
ctx.putImageData(imageDataWrapper, 0, 0);
requestAnimationFrame(animation);
}
textureImage.src = "https://untamed.zone/miscImages/metalEye.jpg";
function unitCircleLut2( resolution ) {
function y( x ) {
// x ** 128 compresses when x approaches 1. This helps accentuate the
// curvature of the sphere near the edges...
return ( Math.PI / 2 - Math.acos( x ** 128 ) ) / ( Math.PI / 2 );
}
let r = resolution / 2 |0;
// Rough calculate the length of the arc...
let arc = new Array( r );
for ( let i = 0; i < r; i++ ) {
// The calculation for nx stretches x when approaching 0. This removes the
// center bubble effect...
let nx = ( i / r ) ** ( 1 / 32 );
arc[ i ] = { x: nx, y: y( nx ), arcLen: 0 };
if ( 0 < i ) {
arc[ i ].arcLen = arc[ i - 1 ].arcLen + Math.sqrt( ( arc[ i ].x - arc[ i - 1 ].x ) ** 2 + ( arc[ i ].y - arc[ i - 1 ].y ) ** 2 );
}
}
let arcLength = arc[ r - 1 ].arcLen;
// Now, for each element in the array, calculate the factor to apply to the
// metal eye to either stretch (center) or compress (edges) the image...
let lut = new Array( resolution );
let centerX = r;
let centerY = r;
for( let y = 0; y < resolution; y++ ) {
let ny = y - centerY;
lut[ y ] = new Array( resolution );
for( let x = 0; x < resolution; x++ ) {
let nx = x - centerX;
let nd = Math.sqrt( nx * nx + ny * ny ) |0;
if ( r <= nd ) {
lut[ y ][ x ] = null;
} else {
lut[ y ][ x ] = arc[ nd ].arcLen / arcLength;
}
}
}
return lut;
}
function drawSphere2(dx, dy){
const mx = textureCanvas.width - c.width;
const my = textureCanvas.height - c.height;
const idx = Math.round(dx);
const idy = Math.round(dy);
const textureCenterX = textureCanvas.width / 2 |0;
const textureCenterY = textureCanvas.height / 2 |0;
let iD32index = 0;
for(let y = 0; y < c.height; y++){
for(let x = 0; x < c.width; x++){
let stretch = offsetLUT[y][x];
if(stretch == null){
imageData32[iD32index++] = 0;
}else{
// The 600/150 is the ratio of the metal eye to the offsetLut. But, since the
// eye doesn't fill the entire image, the ratio is fudged to get more of the
// eye into the sphere...
let tx = ( x - 150 ) * 600/150 * Math.abs( stretch ) + textureCenterX + dx |0;
let ty = ( y - 150 ) * 600/150 * Math.abs( stretch ) + textureCenterY + dy |0;
let textureIndex = tx + ty * textureCanvas.width;
imageData32[iD32index++] = textureData32[textureIndex];
}
}
}
ctx.putImageData(imageDataWrapper, 0, 0);
}
// Move the texture on the sphere and keep redrawing.
function animation(){
px += vx;
py += vy;
let xx = Math.cos(px / 180 * Math.PI) * 180 + 0;
let yy = Math.cos(py / 180 * Math.PI) * 180 + 0;
drawSphere2(xx, yy);
requestAnimationFrame(animation);
}
body {
background: #202020;
color: #f0f0f0;
font-family: arial;
}
canvas {
border: 1px solid #303030;
}
Related
Animated footsteps in html svg
Is it possible to create small animated footsteps in HTML SVG or Canvas. I am just starting out with these technologies, and it is very much necessary for a small project i am working on. My current idea was to create and use a "gif" of animated footsteps. But i would like to know if it can be achieved in any other way through HTML/CSS/JS PS : The footsteps i keep mentioning should be similar to the footsteps that appear on the "Marauders Map" in harry potter Movies. Thanks for any help
Walk about. I have never seen the movie you talk about so I am guessing what you are after. To do it on the canvas is easy, and I am more than happy to write an example of how it's done for you. Draw a foot You need an image of a foot, or some way to draw a foot. I used a set of shapes I happened to have to draw a foot. Then create a function that draws the foot at a location and in a direction. You also need to specify if its a left or right step. If you have a foot image you want to use just replace the code in the drawFoot function after the comment // draw the foot... with ctx.drawImage(footImage,-footImage.width / 2, -footImage.height / 2); You may have to scale the image, and the foot image should be of the left foot toes pointing to the right of screen Path The path to walk along is just an array of points, [x,y,x,y...] in order that define the path to travel along, They are just a guide as each foot step is a fixed distance apart and the points can be any distance appart. Animate Each step is some time apart (demo is 500ms or half a second). I use setTimeout to call the function to draw the next step. When the function is called I find the next position on the path and draw the next foot step. When the walk has gone past the end of the path I clear the canvas and restart. Demo It's self explanatory in the demo. I track two positions on the path, one is where the foot step is, and the other is just ahead and used to get the direction the foot should be drawn. The function that gets the distance along the path is recursive because the path points are not uniform in distance apart, so may have to skip line segments if the distance to travel is greater than the current or next or few line segments. // create a canvas and add it to the DOM var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;} var canvas = createImage(1024,512); var ctx = canvas.ctx; document.body.appendChild(canvas); // shapes for the foot. Left foot const foot = [ [50.86759744022923,-21.798383338076917,54.16000854997335,-23.917474843549847,57.778065334829385,-25.310771525314685,61.579706305344985,-24.823762596975733,65.0168431653022,-22.69100336700319,65.22736925598322,-19.777045647294724,63.09649708656406,-16.826669881834157,59.66512409715465,-15.356252875428147,56.12216018899968,-14.92259970211097,52.06404407417057,-16.231839553378,50.2945579442406,-18.938589556263246], [60.12039562389232,-12.45714817900668,63.92394094034509,-14.994440399059425,68.75013312287521,-15.737202635924493,73.10937504616481,-14.878459739068003,76.36064492186433,-12.559833524837757,77.6863729180915,-9.181208344485064,75.4625672565435,-5.673231626251427,71.79886053681197,-3.7381471592011817,66.8618942467243,-3.4903609701416993,62.29264392518654,-5.1248680438596885,58.98975517513061,-8.760952968033395], [65.57414109270752,1.1797411270282658,69.37768640916029,-1.3575510930244814,74.20387859169041,-2.1003133298895467,78.56312051498001,-1.241570433033059,81.81439039067953,1.0770557811971881,83.14011838690669,4.455680961549877,80.9163127253587,7.963657679783514,77.25260600562717,9.898742146833756,72.3156397155395,10.146528335893239,67.74638939400174,8.512021262175251,64.44350064394581,4.875936338001546], [65.89915812212375,15.917267748549033,69.97688522977245,12.635767809535894,76.25232402290966,11.736330263416008,81.47328014710077,12.477566678331382,86.09545897877996,15.579569438835726,86.99032987455637,21.425830739951824,83.82647235747945,24.97297628549917,79.18353918133074,27.064789354098487,73.69821507617947,27.23418460503707,68.46309469655478,24.972976285499172,64.88602415530316,20.55351481505123], [58.48881251239855,36.09589759380796,65.7080603859302,29.82038065752831,74.19222753183148,28.331948884004674,82.75081761131048,31.085582242549528,88.10923922724437,34.28575070762116,91.45825273720305,41.65358042953028,87.067323913035,47.45853718012533,79.77391671356942,50.28659303297933,71.73628428966856,50.06332546564875,64.14518700042888,47.45853718012533,60.12637078847845,42.69549574373964], [-73.48953442580058,20.579088801900756,-80.48690958722098,13.959950657259256,-81.93681598574045,6.269142804242765,-81.49554012532147,-1.6107832746678348,-77.90207999991593,-9.181527272091415,-71.6611785393426,-14.98115303708649,-64.60076477263831,-17.880965834138024,-57.35123278004056,-19.078714598132443,-50.09111381970131,-19.902008890566687,-42.96765884171789,-19.08249722231963,-35.655087439697766,-18.51514254492067,-28.90987071615029,-18.578181953551955,-21.74774447703016,-19.60773669210723,-14.309090001741001,-20.364210136314323,-6.933479190821032,-21.688037717705875,0.33383104043200396,-22.888118253462963,7.772483543580581,-23.77067027395373,15.274173171058239,-24.338024951681817,22.460665755024706,-24.590182586206954,29.710197747622452,-23.707630865368966,36.8557533915613,-22.565778766908277,44.16832856198768,-19.28772830000901,51.48089996424725,-14.370654426435763,56.713170880643965,-7.499358885625703,60.24016927073608,-0.41616112008138906,61.75311234056134,6.518193267525415,62.38350642684393,13.515567625813127,61.67231220934209,20.50500542283382,59.08769637136125,27.56541945045329,54.35974072401175,34.87799085169213,48.686193947196124,39.41682827314464,41.87793781501736,42.379680478815025,34.313208779263185,43.26223219965301,27.063676786665432,42.25360166155246,19.625026568173826,38.28211891778152,13.457927624759604,31.720792179781256,9.486440924356895,25.290772320002496,6.019273449215143,18.7346738223298,0.21964785513691965,13.565442314564446,-5.832135373466421,9.467880753530935,-12.632472285211591,8.882365666622222,-19.188577231399584,11.277861070017005,-26.31203040678842,14.49287091019486,-32.99420772170462,17.833959567652954,-39.2981485848331,20.670732956060768,-45.854247082486715,23.192309301312157,-52.47338498877162,24.89437333435685,-59.5968381641068,25.020452151619416,-67.33461307855538,23.697386555044208], ] const footScale = 0.2; // how big the foot is const stepLen = 60; // distance between steps var stepCount = 0; // current set num so left and right can be known var stepTime = 500; // time ms between steps // path to walk const path = [56.20191403126277,162.4451015086347,83.38688266344644,172.80127602121507,107.98280549274708,192.86637096042372,121.57528916149568,221.34586055208607,124.81159479691195,256.2979614145819,141.64038410107662,293.8391067854107,168.82535143857336,311.9624183437419,203.77745230106916,336.5583411729056,238.0822920364817,344.9727358249879,278.2124819156436,341.0891690624884,316.40088841355566,329.43846877498976,343.58585575105235,310.6678960895754,370.77082308854904,275.7157952270795,359.12012280105046,244.64726112708325,344.23311687813566,207.10611575625444,355.23655603855093,168.9177092583423,394.0722236635463,137.2019140312628,438.0859803052077,137.84917515834604,487.27782596353507,174.0957982750084,507.9901820301992,221.9931216791693,513.1682710468652,269.243183956247,500.87030963228347,318.43502961457443,480.1579535656192,354.68165273123674,453.62744426338134,396.86543776550707,414.1445200788371,427.9340428271046,372.7198079555102,447.3518767949864,320.2916566617712,442.173787778395,272.39433325761024,427.9340429825634,218.02439858261675,441.5265266513118,185.66134222845398,472.59506075130815,160.418158272207,514.6670340117198,168.2291881671332,557.5405924870362,200.59229872785795,598.9654951914354,232.9553551615553,627.4449850627141,273.08554504131894,651.3936467669101,320.3356073183967,663.0443470544095,368.2329307225576,663.6916081814927,417.4247763808851,649.4518633856611,460.7912718954633,626.150462810664,509.33585642670744,593.1401453294179,530.6954736204549,556.8935222127556,559.9273870166451,517.9197870310509,582.4287517306153,484.11964343037323,597.1560832290169,459.03222473087396,621.0274949086466,438.11039022321154,651.3689440081158,429.43667093843567,686.3731150817684,432.05029606733103,726.1878666750503,421.6902139845064,748.5744620042316,397.8927935245363,778.6337708564557,367.5111094723503,792.6287871481064,335.0802046803193,795.4641381478963,294.8623601925252,790.3177294792127,255.26933447013639,776.3228370821212,225.344431214269,746.3711518226298,192.73203550406103,713.7991149596966,199.06094085265394,674.3068624609349,207.5062077919911,638.4763261746227,190.31310645331482,613.6509940547375,146.74931837304698,621.5992452450397,103.454341485492,665.5124383180124,60.96567428151931,716.1845355322713,48.49595708249788,763.6383682758693,51.23726133320403,810.1045243122669,71.53440096982465,842.407749982487,97.97907893142005,879.5993779794437,131.14717279570036,903.6790094213126,175.24174017377706,915.9471803279671,219.31612086267396,902.1335310600084,270.1561321514687,880.1365756762476,315.0232456643523,884.5103070340778,370.89556334366307,909.7723644212043,407.9691345807976,947.0675376346722,439.8492118274288,990.6429384281869,439.26727537005956,1036.8675099917996,414.23364852545194,1070.3264506272462,387.0500494883014,1100.6074853525497,351.4546920217324,1119.943854180156,306.7958514306488,1128.5371035594999,259.67124425611814,1122.6651029017848,208.79760059460207,1106.8898009575623,162.16340658911932,1082.4004208812885,108.81054339506417,1050.2046949092428,81.72759371897288,1016.627194271211,46.42875173061529]; const pLen = path.length; // fix the path so it starts at zero for(var i = 2; i < pLen; i += 2){ path[i] -= path[0]; path[i+1] -= path[1]; } path[0] = path[1] = 0; // tracks the foot pos var pos = { x : path[0], y : path[1], index : 0, }; // tracks the foot pointing to pos var pos1 = { x : path[0], y : path[1], index : 0, }; // get a distance alone the path function getDistOnPath(dist,pos){ var nx = path[(pos.index + 2) % pLen] - pos.x; var ny = path[(pos.index + 3) % pLen] - pos.y; var d = Math.hypot(nx, ny); if(d > dist){ pos.x += (nx / d) * dist; pos.y += (ny / d) * dist; return pos; } dist -= d; pos.index += 2; pos.x = path[pos.index % pLen]; pos.y = path[(pos.index + 1) % pLen]; return getDistOnPath(dist, pos); } function drawFoot(x,y,dir,left){ var i,j,shape; var xdx = Math.cos(dir) * footScale; var xdy = Math.sin(dir) * footScale; if(left){ ctx.setTransform(xdx, xdy, -xdy, xdx, x + xdy * 50, y - xdx * 50); ctx.rotate(-0.1); // make the foot turn out a bit }else{ ctx.setTransform(xdx, xdy, -xdy, xdx, x - xdy * 50, y + xdx * 50); ctx.rotate(-0.1); // make the foot turn out a bit ctx.scale(1,-1); // right foot needs to be mirrored } // draw the foot as a set of paths points ctx.beginPath(); for(j = 0; j < foot.length; j ++){ shape = foot[j]; i = 0; ctx.moveTo(shape[i++],shape[i++]); while(i < shape.length){ ctx.lineTo(shape[i++],shape[i++]); } ctx.closePath(); } ctx.fill(); } ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,canvas.width,canvas.height); pos1 = getDistOnPath(stepLen/10,pos1); // put the second pos infront so that a direction can be found function drawStep(){ if(pos.index > pLen){ // if past end of path clear and restart ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,canvas.width,canvas.height); pos.index = 0; pos1.index = 0; pos1.x = pos.x = path[0]; pos1.y = pos.y = path[1]; pos1 = getDistOnPath(stepLen/10,pos1); } pos = getDistOnPath(stepLen,pos); pos1 = getDistOnPath(stepLen,pos1); drawFoot(pos.x,pos.y,Math.atan2(pos1.y - pos.y, pos1.x - pos.x),(stepCount++) % 2 === 0); setTimeout(drawStep,stepTime); } drawStep(); Note some of the code is ES6 and will require babel (or equivilent) to run on legacy browsers.
JavaScript canvas, scale between two points on chart/graph?
So I've built a small graph application with JavaScript to help me practice using the canvas. I've spent the last 10 hours trying to scale between two points on the X-Axis and can't for the life of me figure it out. I've learned that to scale you need to translate > scale > translate. This works fine when I scale to the far left/right using the following type code. let x = 0; let y = this.getCanvasHeight() / 2; this.getCanvasContext().clearRect(0, 0, this.getCanvas().width, this.getCanvas().height); this.setCanvas(); ctx.translate(x, y); ctx.scale(scale, 1); ctx.translate(-x, -y); this.resetCanvasLines(); this.renderGraph(this.state.points, scale); This piece of code simply allows me to zoom into the far left of the graph. So now I'm trying to pick two points on this graph and zoom in on top of them, so that they fit evenly on the screen. The Y-Axis will always be the same. My thinking was to get the midpoint between the two points and zoom in on that location, which I feel should work but I just can't get it working. My graph width is 3010px and split into 5 segments of 602px. I want to zoom let's say from x1 = 602 and x2 = 1806, which has the midpoint of 1204. Is there a technique to properly calculating the scale amount? rangeFinder(from, to) { let points = this.state.points; if (points.length === 0) { return; } let ctx = this.getCanvasContext(); let canvasWidth = this.getCanvasWidth(); let canvasHeight = this.getCanvasHeight() / 2; let seconds = this.state.seconds; let second = canvasWidth / seconds; let scale = 1; // My graph starts from zero, goes up to 5 and the values are to represent seconds. // This gets the pixel value for the fromX value. let fromX = from * second; to = isNaN(to) ? 5 : to; // Get the pixel value for the to location. let toX = parseInt(to) * second; let y = canvasHeight / 2; // get the midpoint between the two points. let midpoint = fromX + ((toX - fromX) / 2); // This is where I really go wrong. I'm trying to calculate the scale amount let zoom = canvasWidth - (toX - fromX); let zoomPixel = (zoom / 10) / 1000; let scaleAmount = scale + ((zoom / (canvasWidth / 100)) / 100) + zoomPixel; ctx.clearRect(0, 0, this.getCanvas().width, this.getCanvas().height); this.setCanvas(); // translate and scale. ctx.translate(midpoint, y); ctx.scale(scaleAmount, 1); ctx.translate(-midpoint, -y); this.resetCanvasLines(); this.renderGraph(points); } Any help would be great, thanks.
Scale = 5/3 = total width / part width. After scale, x = 602 should have moved to 602 * 5/3 ~ 1000. Translate the new image by -1000. There is no need to find mid-point.
Animating an object from its center with sine wave in three.js
I am trying to replicate this effect: https://dribbble.com/shots/1754428-Wave?list=users&offset=5 I want to animate a plane's vertices simlarly to the link I've provided. I know that it's achieved using a sine wave propagation, but I can't figure out how to start the movement from the central point of the plane. Right now, I have something like this (function drawFrame(ts){ window.requestAnimationFrame(drawFrame); var vLength = plane.geometry.vertices.length; for (var i = 0; i < vLength; i++) { var v = plane.geometry.vertices[i]; v.z = Math.sin(ts / 500 + (v.x * (vLength / 2)) * (v.y / (vLength / 2))) * 3 + 5; } It works kind of OK, but notice how in the top left and bottom right corners the movement is inward, towards the centre of the plane and not outwards, as it should be. The other two corners are behaving in exactly the way I want them to be. Here's a link to what I currently have: http://codepen.io/gbnikolov/pen/QwjGPg All suggestions and ideas are more then welcome!
I have found the function you are after it was fun! (function drawFrame(ts){ var center = new THREE.Vector2(0,0); window.requestAnimationFrame(drawFrame); var vLength = plane.geometry.vertices.length; for (var i = 0; i < vLength; i++) { var v = plane.geometry.vertices[i]; var dist = new THREE.Vector2(v.x, v.y).sub(center); var size = 5.0; var magnitude = 2.0; v.z = Math.sin(dist.length()/size + (ts/500)) * magnitude; } plane.geometry.verticesNeedUpdate = true; renderer.render(scene, camera); }()); The circular pattern is created by creating a point as I did above called center. This is where the wave originates. We calculate distance to the center point. We then sin the distance from the center point to create the up/down. Next we add the time ts to create the movement. Finally we add some variables to tweak the size of the wave.
Finding a location on cosine curve with a specified distance to another location JS
I am working on a "rally" game where a car is drawing on hills made of cosine curves. I know the current xspeed of the car (without hills) but the problem is that I need to know the xspeed of the car on the hills to be able to draw the wheels on right places and keep the speed steady. At the moment my solution looks like this. function drawWheelOnBasicHill(hillStart, xLocWheel, wheelNro) { var cw = 400 //the width of the hill t_max = 2*Math.PI; var scale = 80, step = cw, inc = t_max/step; var t1 = (xLocWheel-hillStart)*inc var y1 = -scale*0.5 * Math.cos(t1); if(wheelNro == 1 ){ //backwheel drawRotatedImage(wheel, car.wheel1x, car.wheel1y-y1-45,sx); //drawing the wheel on canvas } else { //frontwheel drawRotatedImage(wheel, car.wheel2x, car.wheel2y-y1-45,sx); } for(var i=1; i<=car.speed; i++){ //finding the next xlocation of the wheel with the //same distance (on the curve) to the previous location as the speed of the car(=the //distance to the new point on the flat ground) var t2 = (xLocWheel + i -hillStart)*inc var y2 = -scale*0.5 * Math.cos(t2); if(Math.round(Math.sqrt(i^2+(y2-y1)^2))==car.speed){ sx = sx+i; //the new xcoordinate break; } } } The for loop is the problem. It might bee too slow (animation with fps 24). I cant understand why the if statement isnt working at the moment. It works sometimes but most of the times the value of the condition newer reaches the actual xspeed. Are there some more efficient and easier ways to do this? Or does this code contain some errors? I really appreciate your efforts to solve this! Ive been looking at this piece of code the whole day..
So i is the variable and x2=x1+i t2=t1+i*inc y1=-scale*0.5 * Math.cos(t1) y2=-scale*0.5 * Math.cos(t2) which somehow is strange. The landscape should be time independent, that is, y should be a function of x only. The time step is external, determined by the speed of the animation loop. So a more logical model would have dx as variable and dt = t2-t1 x2 = x1 + dx y1 = f(x1) = -0.5*scale*cos(x1) y2 = f(x2) = -0.5*scale*cos(x2) and you would be looking for the intersection of (x2-x1)^2+(y2-y1)^2 = (speed*dt)^2 which simplifies to (speed*dt)^2=dx^2+0.25*scale^2*(cos(x1+dx)-cos(x1))^2 For small values of dx, which would be the case if dt or speed*dt is small, cos(x1+dx)-cos(x1) is approx. -sin(x1)*dx leading to dx = (speed*dt) / sqrt( 1+0.25*scale^2*sin(x1)^2 ) To get closer to the intersection of curve and circle, you can then iterate the fixed point equation dydx = 0.5*scale*(cos(x1+dx)-cos(x1))/dx dx = (speed*dt) / ( 1+dydx^2 ) a small number of times.
Three.js 2D X, Y coords to 3D X, Y, Z coords always in front of camera
I've looked at this question: Mouse / Canvas X, Y to Three.js World X, Y, Z and have implemented it in my code, the problem is that I can't seem to get it to work as others have stated. I need to place an object in front of the camera via X and Y screen coords, not necessarily from the mouse. This object will be at a specified distance in front of the camera, either from a pre-defined maximum distance or a calculated object distance. Note: My Camera can be at any position in my scene and facing any direction Here is my code: this.reticlePos.x = 500; this.reticlePos.y = 300; this.reticlePos.z = 0.5; this.projector.unprojectVector(this.reticlePos, this.camera); //check for collisions, if collisions set distance to collision distance var distance = this.RETICLE_RADIUS; var direction = new THREE.Vector3( 0, 0, -1 ); direction.applyQuaternion( this.camera.quaternion ); this.rayCaster.set(this.camera.position, direction); var collisionResults = this.rayCaster.intersectObjects(this.sceneController.obstacles); if( collisionResults.length !== 0 ) { // console.log('Ray collides with mesh. Distance :' + collisionResults[0].distance); distance = collisionResults[0].distance - 20; } // set the reticle position var dir = this.reticlePos.clone().sub( this.camera.position ).normalize(); var pos = this.camera.position.clone().add( dir.multiplyScalar( distance ) ); this.reticleMesh.position.copy( pos ); As you can see is is very similar to the linked question, yet I cannot see the object in front of my camera. Any insight into this would be greatly appreciated.
I figured out the answer myself for anyone that checks this question later on. To get my screen coordinates I needed to convert some input data from a gyroscope, and didn't realize that I still needed to do the following to my calculated screen coordinates: this.reticlePos.x = ( this.reticlePos.x / window.innerWidth ) * 2 - 1; this.reticlePos.y = - ( this.reticlePos.y / window.innerHeight ) * 2 + 1; After doing this, everything works as expected.