Plotting a discrete-time signal shows amplitude modulation - graphics

I'm trying to render a simple discrete-time signal using a canvas element. However, the representation seems to be inaccurate. As you can see in the code snippet the signal appears to be amplitude modulated after the frequency reaches a certain threshold. Even though it's well below the Nyquist limit of <50Hz (assuming a sampling rate of 100Hz in this example).
For very low frequencies like 5Hz it looks perfectly fine.
How would I go about rendering this properly? And does it work for more complex signals (say, the waveform of a song)?
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
signal.forEach((value, i) => {
const x = i / (signal.length - 1) * w;
const y = h - (value + 1) / 2 * h;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
#media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>

It looks right to me. At higher frequencies, when the peak falls between two samples, the sampled points can be a lot lower than the peak.
If the signal only has frequencies < Nyquist, then the signal can be reconstructed from its samples. That doesn't mean that the samples look like the signal.
As long as your signal is oversampled by 2x or more (or so), you can draw it pretty accurately by using cubic interpolation between the sample points. See, for example, Catmull-Rom interpolation in here: https://en.wikipedia.org/wiki/Cubic_Hermite_spline
You can use the bezierCurveTo method in HTML Canvas to draw these interpolated curves. If you need to use lines, then you should find any maximum or minimum points that occur between samples and include those in your path.
I've edited your snippet to use the bezierCurveTo method with Catmull-Rom interpolation below:
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
const dx = w/(signal.length - 1);
const dy = -(h-2)/2.0;
const c = 1.0/2.0;
for (let i=0; i < signal.length-1; ++i) {
const x0 = i * dx;
const y0 = h*0.5 + signal[i]*dy;
const x3 = x0 + dx;
const y3 = h*0.5 + signal[i+1]*dy;
let x1,y1,x2,y2;
if (i>0) {
x1 = x0 + dx*c;
y1 = y0 + (signal[i+1] - signal[i-1])*dy*c/2;
} else {
x1 = x0;
y1 = y0;
ctx.moveTo(x0, y0);
}
if (i < signal.length-2) {
x2 = x3 - dx*c;
y2 = y3 - (signal[i+2] - signal[i])*dy*c/2;
} else {
x2 = x3;
y2 = y3;
}
ctx.bezierCurveTo(x1,y1,x2,y2,x3,y3);
}
ctx.stroke();
}
#media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>

Related

Phaser Scrollable Text Box Tutorial not working on mobile

I recreated the scrolling text box tutorial in my game. However, it is running a bit glitchy on mobile. For example, if I swipe up, the text first goes down for a second and then follows my finger up. You’ll see the problem if you open the tutorial on mobile. Any thoughts? I copied my code below.
var graphics = scene.make.graphics();
graphics.fillRect(x, y + 10, width, height - 20);
var mask = new Phaser.Display.Masks.GeometryMask(scene, graphics);
var text = scene.add.text(x + 20, y + 20, content, {
fontFamily: 'Assistant',
fontSize: '28px',
color: '#000000',
wordWrap: { width: width - 20 }
}).setOrigin(0);
text.setMask(mask);
var minY = height - text.height - 20;
if (text.height <= height - 20) {
minY = y + 20;
}
// The rectangle they can 'drag' within
var zone = scene.add.zone(x, y - 3, width, height + 6).setOrigin(0).setInteractive({useHandCursor: true});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
text.y += (pointer.velocity.y / 10);
text.y = Phaser.Math.Clamp(text.y, minY, y + 20);
}
});
I had the same issue. My solution is using "pointer.y" instead of "pointer.velocity.y".
Here is my code:
var previousPointerPositionY;
var currentPointerPositionY;
zone.on('pointerdown', function (pointer) {
previousPointerPositionY = pointer.y;
});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
currentPointerPositionY = pointer.y;
if(currentPointerPositionY > previousPointerPositionY){
text.y += 5;
} else if(currentPointerPositionY < previousPointerPositionY){
text.y -= 5;
}
previousPointerPositionY = currentPointerPositionY;
text.y = Phaser.Math.Clamp(text.y, -360, 150);
}
});

How to rotate cubee by quaternion in three.js?

I have some problems with understanding of how to rotate the figure by a quaternion. Can somebody please explain how to do it? In function render I want to rotate cubes by quaternions
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 100;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 3;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
function makeInstance(color, x, width, height, depth) {
const material = new THREE.MeshPhongMaterial({color});
const geometry = new THREE.BoxGeometry(width, height, depth);
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
return cube;
}
const cubes = [
makeInstance(0x8844aa, -2, 3, 1, 1),
makeInstance(0xaa8844, 0.5, 2, 1, 1),
];
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
// cubes.forEach((cube, ndx) => {
//const speed = 1 + ndx * .1;
//const rot = time * speed;
//cube.rotation.x = rot;
//cube.rotation.y = rot;
//});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
You have an Object3d (Points, Lines, Meshes, etc.) that you want to rotate via quaternions. You have a mesh (the cube). The immediate answer is to:
cube.applyQuaternion(myquat);
And where does myquat come from? Probably from one of these:
myquat = new THREE.Quaternion(); // now, Probably from one of these:
myquat.setFromAxisAngle ( axis : Vector3, angle : Float )
myquat.setFromEuler ( euler : Euler )
myquat.setFromRotationMatrix ( m : Matrix4 )
myquat.setFromUnitVectors ( vFrom : Vector3, vTo : Vector3 )
I hope this gives you a start, even to ask a more specific question.

Animating lots of lines in WebGL

I'm trying to animate this scene at 1280x720, 60 fps, antialiased. There are ~500K triangles, two for each tree branch. I can't use gl.LINES because ANGLE.
To get a rough idea about the performance I can expect, I made this test with 1M of random triangles:
<!doctype html>
<head>
<title>Triangles</title>
<style>
html, body {
background: #000;
height: 100%;
margin: 0;
}
canvas {
width: 1280px;
height: 720px;
position: absolute;
margin: auto;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
</style>
</head>
<body>
<script>
'use strict';
const triangleCount = 1e6;
const antialias = true;
const generateTriangles = (count, width, height) => {
const coords = new Float32Array(9 * count);
for (var i = 0; i < coords.length;) {
const x = Math.random() * 2 - 1;
const y = Math.random() * 2 - 1;
const z = Math.random() * 2 - 1;
const theta = Math.random() * Math.PI;
const ax = 10 * Math.cos(theta) / width;
const ay = 10 * Math.sin(theta) / height;
const bx = 10 * Math.cos(theta + 0.3) / width;
const by = 10 * Math.sin(theta + 0.3) / height;
coords[i++] = x + ax; coords[i++] = y + ay; coords[i++] = z;
coords[i++] = x + bx; coords[i++] = y + by; coords[i++] = z;
coords[i++] = x - ax; coords[i++] = y - ay; coords[i++] = z;
coords[i++] = x - ax; coords[i++] = y - ay; coords[i++] = z;
coords[i++] = x - bx; coords[i++] = y - by; coords[i++] = z;
coords[i++] = x + ax; coords[i++] = y + ay; coords[i++] = z;
}
return coords;
};
const vertexShaderSource = `
precision lowp float;
attribute vec3 aPosition;
uniform float uWobble;
void main() {
float p = 1.0 / (0.3 * aPosition.z - 1.4 + 0.1 * uWobble);
gl_Position = vec4(p * aPosition.x, p * aPosition.y, aPosition.z, 1.0);
}
`;
const fragmentShaderSource = `
precision lowp float;
void main() {
float z = gl_FragCoord.z;
gl_FragColor = vec4(1.2 * z, z * z, z, 1.0);
}
`;
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const gl = canvas.getContext('webgl', {alpha: false, antialias});
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const aVertexPosition = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(aVertexPosition);
const uWobble = gl.getUniformLocation(program, 'uWobble');
gl.uniform1f(uWobble, 1);
const vertices = generateTriangles(triangleCount, canvas.width, canvas.height);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
const render = (timestamp) => {
requestAnimationFrame(render);
gl.uniform1f(uWobble, Math.sin(0.002 * timestamp));
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 3);
};
window.requestAnimationFrame(render);
</script>
</body>
On a Macbook with Iris 1536 MB I'm getting 12 frames per second. Are there any optimizations I'm missing, such as alternative ways to draw lines, faster antialiasing, some magic flags? Or is this basically the limit of what one can expect from an average GPU?

vtk and three.js synchronization

I'm working on a web application that allows you to send from the server to the client a picture made ​​with VTK. But how do you know the speed of the Internet depends on the speed, well, the file system. And to speed up the work, I decided to add to the client webGL. And now the question arose in the data synchronization. In VTK rotates the camera around the object, after many attempts, I could not do on the client side, the camera works well (with the change of coordinates the camera), but was able to make correct the rotation of the object. In this some questions:
1. is any ideas with camera?
2. or it will be easy to change rotation in vtk?
Update:
I make a camera and it's synchroniz with vtk, but know i have a problem with viewUp vector, on client after rising phi more than 90 it's became (0,-1,0) and when i make SetViewUp(0,-1,0) nothing changed
Javascript:
renderer.domElement.addEventListener('mousemove', function(e) {
e.preventDefault();
if ( paused ) {
return;
}
if ( !moving ) {
lastLeft = e.clientX;
lastTop = e.clientY;
moving = true;
}
// horizontal
theta = - ( ( event.clientX - lastLeft ) * 360 /window.innerWidth ) + onMouseDownTheta;
phi = ( ( event.clientY - lastTop ) * 360 /window.innerHeight ) + onMouseDownPhi;
//phi = Math.min( 90, Math.max( -90, phi ) );
var cosPhi = Math.cos( phi * Math.PI / 180 );
var sinPhi = Math.sin( phi * Math.PI / 180 );
var sinTheta = Math.sin( theta * Math.PI / 180 );
var cosTheta = Math.cos( theta * Math.PI / 180 );
//trace('data_move');trace(radious);trace(theta);trace(phi);trace(camera.up);
camera.position.x = radious * cosTheta * cosPhi;
camera.position.y = radious * sinPhi;
camera.position.z = radious * sinTheta * cosPhi;
var u;
if (phi<0) phi=2*180+phi;
if (phi>2*180) phi=phi-2*180;
if ((phi>180/2)&&(phi<3*180/2))
u=-1;
else
u=1;
camera.up = new THREE.Vector3(0, u, 0);
camera.lookAt(new THREE.Vector3(FocalPoint[0],FocalPoint[1],FocalPoint[2]))
trace('data_move');trace(FocalPoint);trace(camera.up);
camera.updateMatrix();
});
renderer.domElement.addEventListener('mousedown', function(e) {
paused = false;
lastLeft = e.clientX;
lastTop = e.clientY;
onMouseDownTheta = theta;
onMouseDownPhi = phi;
});
renderer.domElement.addEventListener('mouseup', function(e) {
moving = paused = true;
counter = 0
trace('data_move');trace(camera.position);trace(camera.up);
comm.makeRequest("vtk", {
mode: curVtkConf.mode,
command: "set_camera",
up: {'x': camera.up.x,'y': camera.up.y,'z': camera.up.z},
position: {'x': camera.position.x,'y': camera.position.y,'z': camera.position.z},
});
});
Phyton:
def set_camera(self, position, up):
'''
Поворот в жестко заданную позицию.
'''
self.move_to_origin()
bounds = self.src_actor.GetBounds()
camera = self.renderer.GetActiveCamera()
x, y, z = camera.GetPosition()
edge = None
camera.SetPosition(position['x'], position['y'], position['z'])
camera.SetViewUp(up['x'], up['y'], up['z'])
self.world_coords_to_pixel_ratio = self._get_world_coords_to_pixel_ratio()
camera.OrthogonalizeViewUp()
camera.SetRoll(0)
self.renderer.ResetCameraClippingRange()
self.renderer.ResetCamera()
Update2:
Now also i have some problems with rotation. The model just changed it size while rotating
Solved: by moving model to the center
Solved at all))

Flip shape with image attached using RaphaelJS

I managed to create a hexagon with Raphel, gave it a stroke and a fill image, then (with help from StackOverFlow) managed to create a flip animation on the hexagon.
But I am facing a problem. When the hexagon flips, the background image does not flip with it. How can I make the hexagon shape flip, but as it is flipping, show the background image flipping too.
Here is what I currently have:
function polygon(x, y, size, sides, rotate) {
var self = this;
self.centrePoint = [x,y];
self.size = size;
self.sides = sides;
self.rotated = rotate;
self.sizeMultiplier = 50;
self.points = [];
for (i = 0; i < sides; i++) {
self.points.push([(x + (self.size * self.sizeMultiplier) * (rotate ? Math.sin(2 * 3.14159265 * i / sides) : Math.cos(2 * 3.14159265 * i / sides))), (y + (self.size * self.sizeMultiplier) * (rotate ? Math.cos(2 * 3.14159265 * i / sides) : Math.sin(2 * 3.14159265 * i / sides)))]);
}
self.svgString = 'M' + self.points.join(' ') + ' L Z';
}
$(document).ready(function() {
var paper = Raphael(0, 0, 450, 450);
var path1 = new polygon(100, 100, 2, 6, false);
var hex1 = paper.path(path1.svgString);
hex1.node.id = "hex1";
hex1.attr("fill", "url('http://i49.tinypic.com/23ma7pt.jpg')");
hex1.attr("stroke", "#aceace");
/* flip animation */
hex1.click(function() {
hex1.animate({transform: "S0,1"},500,'easeIn', function()
{
hex1.attr("fill","url('http://i49.tinypic.com/23ma7pt.jpg')");
hex1.animate({transform: "S-1,1"},500,'easeOut');
});
});
});

Resources