<style>
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
:focus {
outline: 0;
}
/* END RESET */
#presets,
#cameraPresets,
#numberOfFreqs {
display: inline-block;
margin-left: 1em;
}
button {
border: 0;
background: #ddd;
color: #000;
padding: 5px 10px;
font-weight: bold;
font-size: 12pt;
border-radius: 5px;
cursor: pointer;
margin-left: 0.5em;
}
#input {
margin: 20px;
display: grid;
grid-template-columns: 5em 1fr 1fr;
}
#input .head {
font-weight: bold;
text-align: center;
}
#input .frequency {
text-align: right;
padding-right: 0.3em;
}
canvas {
margin-top: 20px;
width: 100%;
background: #fff;
}
#out1 {
width: 400px;
}
</style>
Zeit-/Frequenzdomäne
Im folgenden Applett kann man das ausprobieren: Mit den Frequenzreglern kann man die Amplitude der einzelnen Sinusschwingungen erhöhen. Mit dem Preset "Rechteck" kann man sehen, dass ein Rechteck aus vielen Sinusschwingungen besteht.
Presets:
Ansicht:
Anzahl Frequenzen:
<script src="/lernen/three/three.js"></script>
<script>
var amplitudesSquare = [];
for (var i = 0; i < 30; i++) {
if (i % 2 != 0) amplitudesSquare[i] = 0;
else amplitudesSquare[i] = 1/(i + 1);
}
const presets = [
{
label: "Minimum",
defaultAmplitude: 0,
amplitudes: [],
defaultPhase: 0,
phases: []
},
{
label: "Maximum",
defaultAmplitude: 1,
amplitudes: [],
defaultPhase: 0,
phases: []
},
{
label: "Rechteck",
defaultAmplitude: 0,
amplitudes: amplitudesSquare,
defaultPhase: 0,
phases: []
}
];
const cameraPresets = [
{ label: "Standard", camera: 0, d: 3, theta: 90, phi: 48 },
{ label: "Zeit", camera: 0, d: 3, theta: 20, phi: 48 },
{ label: "Frequenz", camera: 0, d: 3, theta: 160, phi: 48 },
// { label: "Zeit", camera: 1, d: 1, theta: 90, phi: 40 },
// { label: "Frequenz", camera: 1, d: 1, theta: 90, phi: 0 }
];
const steps = 200;
var numOfFreqs;
var lines = [];
var timeline;
var freqlines = [];
var amplitudes = [];
var phases = [];
var cameras = [];
var camera;
var isMouseDown = false;
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
var aspect = 2;
var d = 3;
var theta = 90;
var oldTheta;
var phi = 48;
var oldPhi;
cameras[0] = new THREE.PerspectiveCamera( 25, 2, 0.1, 1000 );
cameras[1] = new THREE.OrthographicCamera(- 0.5 * aspect, 0.5 * aspect, 0.5, -0.5, 0.01, 1000 );
camera = cameras[0];
const canvas = document.querySelector("canvas")
const renderer = new THREE.WebGLRenderer({
premultipliedAlpha: false,
antialias: true,
canvas: canvas
});
function cameraPresetClickedCallback(cameraPreset) {
return function() {
camera = cameras[cameraPreset.camera];
d = cameraPreset.d;
theta = cameraPreset.theta;
phi = cameraPreset.phi;
render();
}
}
function presetClickedCallback(i) {
return function() {
const preset = presets[i];
for (var j = 0; j < numOfFreqs; j++) {
var amplitude = preset.amplitudes[j];
if (! amplitude)
amplitude = preset.defaultAmplitude;
amplitudes[j] = amplitude;
document.getElementById('amplitude' + j).value = amplitude;
var phase = preset.phases[j];
if (! phase)
phase = preset.defaultPhase;
phases[j] = phase;
document.getElementById('phase' + j).value = phase;
}
render();
}
}
function inputChangeCallback(i, norender) {
return function() {
amplitudes[i] = document.getElementById('amplitude' + i).value;
phases[i] = document.getElementById('phase' + i).value;
if (lines[0] && ! norender) {
render();
}
}
}
function initInput() {
const numOfFreqsEl = document.getElementById("numOfFreqs");
numOfFreqsEl.addEventListener("input", function() {
const numOfFreqsEl = document.getElementById("numOfFreqs");
numOfFreqs = numOfFreqsEl.value;
initFreqInput(true);
initScene();
render();
})
const cameraPresetsEl = document.getElementById("cameraPresets");
for (var i = 0; i < cameraPresets.length; i++) {
const cameraPreset = cameraPresets[i];
const el = document.createElement("button");
cameraPresetsEl.appendChild(el);
el.innerHTML = cameraPreset.label;
el.addEventListener("click", cameraPresetClickedCallback(cameraPreset));
}
const presetsEl = document.getElementById("presets");
for (var i = 0; i < presets.length; i++) {
const preset = presets[i];
const el = document.createElement("button");
presetsEl.appendChild(el);
el.innerHTML = preset.label;
el.addEventListener("click", presetClickedCallback(i));
}
numOfFreqs = numOfFreqsEl.value;
initFreqInput();
}
function initFreqInput(norender = false) {
const inputEl = document.getElementById("input");
inputEl.innerHTML = '<div class="head"></div><div class="head">Amplitude</div><div class="head">Phase</div>';
for (var i = 0; i < numOfFreqs; i++) {
const labelEl = document.createElement("div");
inputEl.appendChild(labelEl);
labelEl.classList.add("frequency");
labelEl.innerHTML = i + 1 + " Hz";
const range1El = document.createElement("input");
inputEl.appendChild(range1El);
range1El.type = "range";
range1El.id = "amplitude" + i;
range1El.min = 0;
range1El.max = 1;
range1El.step = 0.01;
var amplitude = amplitudes[i];
if (!amplitude) amplitude = 0;
range1El.value = amplitude;
range1El.addEventListener("input", inputChangeCallback(i));
const range2El = document.createElement("input");
inputEl.appendChild(range2El);
range2El.type = "range";
range2El.id = "phase" + i;
range2El.min = 0;
range2El.max = 1;
range2El.step = 0.01;
var phase = phases[i];
if (!phase) phase = 0;
range2El.value = phase;
range2El.value = 0;
range2El.addEventListener("input", inputChangeCallback(i));
inputChangeCallback(i, norender)();
}
}
function line(x1, y1, z1, x2, y2, z2, material) {
const points = [];
points.push( new THREE.Vector3( x1, y1, z1 ) );
points.push( new THREE.Vector3( x2, y2, z2 ) );
const geometry = new THREE.BufferGeometry().setFromPoints( points );
const line = new THREE.Line( geometry, material );
scene.add(line);
return line;
}
function updateLine(i) {
var line = lines[i];
var freqline = freqlines[i];
var z = 1 - (i + 1) / numOfFreqs;
var minY = 0.5;
var maxY = 0.5;
for (var j = 0; j <= steps; j++) {
var step = 1 / steps;
var x = j * step;
var y = signal(i, x);
if (y > maxY) maxY = y;
if (y < minY) minY = y;
line.geometry.attributes.position.array[j * 3] = x;
line.geometry.attributes.position.array[j * 3 + 1] = y;
}
line.geometry.attributes.position.needsUpdate = true;
freqline.geometry.attributes.position.array[1] = minY;
freqline.geometry.attributes.position.array[4] = maxY;
freqline.geometry.attributes.position.needsUpdate = true;
}
function updateLines() {
for (var i = 0; i < numOfFreqs; i++) {
updateLine(i);
}
for (var j = 0; j <= steps; j++) {
var step = 1 / steps;
var x = j * step;
var y = 0;
for (var i = 0; i < numOfFreqs; i++) {
y += signalFunc(i, x);
}
y = y / 20 + 0.5
timeline.geometry.attributes.position.array[j * 3] = x;
timeline.geometry.attributes.position.array[j * 3 + 1] = y;
timeline.geometry.attributes.position.array[j * 3 + 2] = 1.1;
}
timeline.geometry.attributes.position.needsUpdate = true;
}
function signalFunc(freq, x) {
phase = phases[freq];
amplitude = amplitudes[freq];
return Math.sin(((freq + 1) * x - phase) * 2 * Math.PI) * amplitude;
}
function signal(freq, x) {
return signalFunc(freq, x) / 20 + 0.5
}
function initScene() {
while(scene.children.length > 0){
scene.remove(scene.children[0]);
}
lines = [];
freqlines = [];
const materialBlack = new THREE.LineBasicMaterial({
color: 0x666666
});
const materialBlue = new THREE.LineBasicMaterial({
linewidth: 2,
color: 0x0000ff
});
const materialRed = new THREE.LineBasicMaterial({
linewidth: 2,
color: 0xff0000
});
const plane_material = new THREE.MeshBasicMaterial({
color: 0xcccccc,
opacity: 0.7,
transparent: true,
side: THREE.DoubleSide
});
for (var i = 0; i < numOfFreqs; i++) {
var z = 1 - (i + 0.5) / numOfFreqs;
line(0, 0.5, z, 1, 0.5, z, materialBlack);
const points = [];
for (var j = 0; j <= steps; j++) {
points.push( new THREE.Vector3( 0, 0, z) );
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const l = new THREE.Line(geometry, materialBlue);
scene.add(l);
lines[i] = l;
freqlines[i] = line(1.1, 0.5, z, 1.1, 0.5, z, materialRed);
}
const timepoints = [];
for (var j = 0; j <= steps; j++) {
timepoints.push( new THREE.Vector3( 0, 0, 0) );
}
const timegeometry = new THREE.BufferGeometry().setFromPoints(timepoints);
timeline = new THREE.Line(timegeometry, materialRed);
scene.add(timeline);
line(0, 0.5, 1.1, 1, 0.5, 1.1, materialBlack);
const freqpoints = [];
for (var j = 0; j < numOfFreqs; j++) {
freqpoints.push( new THREE.Vector3( 0, 0, 0) );
}
const freqgeometry = new THREE.BufferGeometry().setFromPoints(freqpoints);
freqline = new THREE.Line(freqgeometry, materialRed);
scene.add(freqline);
line(1.1, 0.5, 0, 1.1, 0.5, 1, materialBlack);
const plane_geometry = new THREE.PlaneGeometry( 1, 0.5 );
const planeX = new THREE.Mesh( plane_geometry, plane_material );
planeX.position.x = 0.5;
planeX.position.y = 0.5;
planeX.position.z = 1.1;
scene.add( planeX );
const planeY = new THREE.Mesh( plane_geometry, plane_material );
planeY.position.x = 1.1;
planeY.position.y = 0.5;
planeY.position.z = 0.5;
planeY.rotation.y = Math.PI / 2 ;
scene.add( planeY );
updateLines();
const canvas_geometry = canvas.getBoundingClientRect();
renderer.setSize(
Math.round(canvas_geometry.width),
Math.round(canvas_geometry.height)
);
canvas.addEventListener("mousedown", function(event) {
isMouseDown = event;
oldTheta = theta;
oldPhi = phi;
});
window.addEventListener("mousemove", function(event) {
if (isMouseDown) {
diffX = event.clientX - isMouseDown.clientX;
diffY = event.clientY - isMouseDown.clientY;
theta = oldTheta - diffX * 0.5;
phi = oldPhi + diffY * 0.5;
phi = Math.min(90, Math.max(-50, phi));
render();
}
});
window.addEventListener("mouseup", function(event) {
isMouseDown = false;
});
}
function render() {
updateLines();
// theta += 0.1;
// camera.position.x = radius * Math.sin( THREE.MathUtils.degToRad( theta ) );
// camera.position.y = radius * Math.sin( THREE.MathUtils.degToRad( theta ) );
// camera.position.z = radius * Math.cos( THREE.MathUtils.degToRad( theta ) );
camera.position.x = d * Math.sin(theta * Math.PI / 360)
* Math.cos(phi * Math.PI / 360);
camera.position.y = d * Math.sin(phi * Math.PI / 360);
camera.position.z = d * Math.cos(theta * Math.PI / 360)
* Math.cos(phi * Math.PI / 360);
camera.updateMatrix();
target_position = new THREE.Vector3(0.5,0.25,0.5);
camera.lookAt( target_position );
renderer.render( scene, camera );
}
initInput();
initScene();
render();
</script>