|
|
|
<!-- Copyright 2023 by Alexei Bezborodov <AlexeiBv+mirocod_wave_simulate@narod.ru> -->
|
|
|
|
<!-- public domain -->
|
|
|
|
|
|
|
|
<html lang="ru">
|
|
|
|
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<title>Симуляция волн</title>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Загрузка скриптов из интернета
|
|
|
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.8.9/brython.min.js"></script>
|
|
|
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.8.9/brython_stdlib.js"></script>
|
|
|
|
-->
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Загрузка скриптов из локальной папки (рядом с файлом html)
|
|
|
|
-->
|
|
|
|
<script type="text/javascript" src="brython.min.js"></script>
|
|
|
|
<script type="text/javascript" src="brython_stdlib.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
<body onload="brython()">
|
|
|
|
|
|
|
|
<details open>
|
|
|
|
<summary>Расчётные данные</summary>
|
|
|
|
<div>
|
|
|
|
<p>
|
|
|
|
<input type="range" id="max_step_range" for="max_step" name="volume" min="10" max="1000" value="20"
|
|
|
|
oninput='document.getElementById("max_step").value = document.getElementById("max_step_range").value;'>
|
|
|
|
<input id = "max_step" type="number" value="20" step="1"
|
|
|
|
oninput='recalculate();'/>
|
|
|
|
<label for="max_step" alt="Количество расчётных точек на всю длину среды">Точек расчёта</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="checkbox" id="right_second" />
|
|
|
|
<label for="right_second" alt="">Вторая половина справа - другая среда</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="checkbox" id="generate" checked />
|
|
|
|
<label for="generate" alt="">Генерирование волны слева</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="checkbox" id="vel_graph_on" checked />
|
|
|
|
<label for="vel_graph_on" alt="">График скорости</label>
|
|
|
|
<input type="range" id="vel_graph_scale_range" for="omega" name="volume" min="0" max="10000" value="300"
|
|
|
|
oninput='document.getElementById("vel_graph_scale").value = document.getElementById("vel_graph_scale_range").value / 100;'>
|
|
|
|
<input id = "vel_graph_scale" type="number" value="3.0" step=".1"/>
|
|
|
|
<label for="vel_graph_scale" alt="">Маштаб</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="checkbox" id="force_graph_on" checked />
|
|
|
|
<label for="force_graph_on" alt="">График сил</label>
|
|
|
|
<input type="range" id="force_graph_scale_range" for="omega" name="volume" min="0" max="10000" value="3000"
|
|
|
|
oninput='document.getElementById("force_graph_scale").value = document.getElementById("force_graph_scale_range").value / 100;'>
|
|
|
|
<input id = "force_graph_scale" type="number" value="30.0" step=".1"/>
|
|
|
|
<label for="force_graph_scale" alt="">Маштаб</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="range" id="length_range" for="omega" name="volume" min="0" max="200" value="50"
|
|
|
|
oninput='document.getElementById("length").value = document.getElementById("length_range").value / 100;'>
|
|
|
|
<input id = "length" type="number" value="0.5" step=".01"/>
|
|
|
|
<label for="length" alt="">Длина волны</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="range" id="omega_range" for="omega" name="volume" min="-1000" max="1000" value="10"
|
|
|
|
oninput='document.getElementById("omega").value = document.getElementById("omega_range").value / 100;'>
|
|
|
|
<input id = "omega" type="number" value="0.1" step=".01"/>
|
|
|
|
<label for="omega" alt="">Шаг фазы</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="range" id="acsel_range" for="omega" name="volume" min="0" max="1000" value="2"
|
|
|
|
oninput='document.getElementById("acsel").value = document.getElementById("acsel_range").value / 100;'>
|
|
|
|
<input id = "acsel" type="number" value="0.02" step=".01"/>
|
|
|
|
<label for="acsel" alt="">Ускрение слева</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="range" id="acsel_second_range" for="omega" name="volume" min="0" max="1000" value="1"
|
|
|
|
oninput='document.getElementById("acsel_second").value = document.getElementById("acsel_second_range").value / 100;'>
|
|
|
|
<input id = "acsel_second" type="number" value="0.001" step=".01"/>
|
|
|
|
<label for="acsel_second" alt="">Ускрение справа</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p>
|
|
|
|
<input type="range" id="trenie_range" for="omega" name="volume" min="0" max="1000" value="980"
|
|
|
|
oninput='document.getElementById("trenie").value = document.getElementById("trenie_range").value / 100;'>
|
|
|
|
<input id = "trenie" type="number" value="0.98" step=".01"/>
|
|
|
|
<label for="trenie" alt="">Трение</label>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<button id="reload">Загрузить</button>
|
|
|
|
</div>
|
|
|
|
</details>
|
|
|
|
|
|
|
|
<h1 class="text-center">Графики волн</h1>
|
|
|
|
<canvas id="draw-board" width="1024" height="500"></canvas>
|
|
|
|
|
|
|
|
<script type="text/python">
|
|
|
|
|
|
|
|
from browser import document, html, window
|
|
|
|
import random, math
|
|
|
|
|
|
|
|
canvas = document["draw-board"]
|
|
|
|
ctx = canvas.getContext("2d")
|
|
|
|
|
|
|
|
x_border = 10
|
|
|
|
y_border = 10
|
|
|
|
|
|
|
|
max_step = 0
|
|
|
|
|
|
|
|
generate = False
|
|
|
|
right_second = False
|
|
|
|
|
|
|
|
length = 0.0
|
|
|
|
omega = 0.0
|
|
|
|
delta_omega = 0.0
|
|
|
|
|
|
|
|
trenie = 0.0
|
|
|
|
acsel = 0.0
|
|
|
|
acsel_second = 0.0
|
|
|
|
|
|
|
|
vel_graph_on = False
|
|
|
|
vel_graph_scale = 0.0
|
|
|
|
|
|
|
|
force_graph_on = False
|
|
|
|
force_graph_scale = 0.0
|
|
|
|
|
|
|
|
y_scale = 0.3
|
|
|
|
|
|
|
|
y_values = []
|
|
|
|
vel_values = []
|
|
|
|
|
|
|
|
# Чёрный экран
|
|
|
|
def DrawBlackScreen():
|
|
|
|
ctx.fillStyle = "black"
|
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
|
|
|
|
def DrawLine(x1, y1, x2, y2, lineWidth, style):
|
|
|
|
ctx.strokeStyle = style
|
|
|
|
ctx.lineWidth = lineWidth
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.moveTo(x1, y1)
|
|
|
|
ctx.lineTo(x2, y2)
|
|
|
|
ctx.stroke()
|
|
|
|
return
|
|
|
|
|
|
|
|
def DrawAxesScreen():
|
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
x1 = x_border
|
|
|
|
x2 = canvas.width - x1
|
|
|
|
y1 = y_border
|
|
|
|
y2 = canvas.height - y1
|
|
|
|
ym = canvas.height/2
|
|
|
|
style = 'red'
|
|
|
|
lw = 2
|
|
|
|
DrawLine(x1, ym, x2, ym, lw, style)
|
|
|
|
DrawLine(x1, y1, x1, y2, lw, style)
|
|
|
|
DrawLine(x2, y1, x2, y2, lw, style)
|
|
|
|
|
|
|
|
style1 = 'blue'
|
|
|
|
base_y = ym - (ym - y1) * y_scale * 1
|
|
|
|
DrawLine(x1, base_y, x2, base_y, lw, style1)
|
|
|
|
|
|
|
|
base_y = ym - (ym - y1) * y_scale * (-1)
|
|
|
|
DrawLine(x1, base_y, x2, base_y, lw, style1)
|
|
|
|
|
|
|
|
|
|
|
|
def base_func(length, omega):
|
|
|
|
return math.sin(length * omega)
|
|
|
|
|
|
|
|
def reload():
|
|
|
|
global max_step
|
|
|
|
global y_values
|
|
|
|
global vel_values
|
|
|
|
global generate
|
|
|
|
global right_second
|
|
|
|
global length
|
|
|
|
global delta_omega
|
|
|
|
global trenie
|
|
|
|
global acsel
|
|
|
|
global acsel_second
|
|
|
|
|
|
|
|
global vel_graph_on
|
|
|
|
global vel_graph_scale
|
|
|
|
|
|
|
|
global force_graph_on
|
|
|
|
global force_graph_scale
|
|
|
|
|
|
|
|
max_step = int(document["max_step"].value)
|
|
|
|
y_values = [0.0] * max_step
|
|
|
|
vel_values = [0.0] * max_step
|
|
|
|
|
|
|
|
generate = document["generate"].checked
|
|
|
|
right_second = document["right_second"].checked
|
|
|
|
|
|
|
|
length = float(document["length"].value)
|
|
|
|
delta_omega = float(document["omega"].value)
|
|
|
|
trenie = float(document["trenie"].value)
|
|
|
|
acsel = float(document["acsel"].value)
|
|
|
|
acsel_second = float(document["acsel_second"].value)
|
|
|
|
|
|
|
|
vel_graph_on = document["vel_graph_on"].checked
|
|
|
|
vel_graph_scale = float(document["vel_graph_scale"].value)
|
|
|
|
force_graph_on = document["force_graph_on"].checked
|
|
|
|
force_graph_scale = float(document["force_graph_scale"].value)
|
|
|
|
|
|
|
|
def recalculate():
|
|
|
|
global max_step
|
|
|
|
global y_values
|
|
|
|
global vel_values
|
|
|
|
global generate
|
|
|
|
global right_second
|
|
|
|
global length
|
|
|
|
global delta_omega
|
|
|
|
global trenie
|
|
|
|
global acsel
|
|
|
|
global acsel_second
|
|
|
|
|
|
|
|
global vel_graph_on
|
|
|
|
global vel_graph_scale
|
|
|
|
|
|
|
|
global force_graph_on
|
|
|
|
global force_graph_scale
|
|
|
|
|
|
|
|
global omega
|
|
|
|
|
|
|
|
DrawBlackScreen()
|
|
|
|
DrawAxesScreen()
|
|
|
|
|
|
|
|
pi = math.pi
|
|
|
|
|
|
|
|
x1 = x_border
|
|
|
|
x2 = canvas.width - x1
|
|
|
|
y1 = y_border
|
|
|
|
y2 = canvas.height - y1
|
|
|
|
ym = canvas.height/2
|
|
|
|
style = 'white'
|
|
|
|
style_vel = 'green'
|
|
|
|
style_force = 'red'
|
|
|
|
line_width_scale = 4.0
|
|
|
|
|
|
|
|
cur_x = 0
|
|
|
|
prev_x = cur_x
|
|
|
|
|
|
|
|
omega += delta_omega
|
|
|
|
|
|
|
|
for i in range(max_step):
|
|
|
|
cur_x = x1 + (x2 - x1) * i / max_step
|
|
|
|
|
|
|
|
if i == 0 and generate:
|
|
|
|
y_values[i] = base_func(length, omega)
|
|
|
|
vel_values[i] = 0.0
|
|
|
|
else:
|
|
|
|
force = 0.0
|
|
|
|
if i != max_step - 1:
|
|
|
|
ac = acsel
|
|
|
|
if right_second and i > max_step / 2:
|
|
|
|
ac = acsel_second
|
|
|
|
force = (y_values[i - 1] - y_values[i]) * ac + (y_values[i + 1] - y_values[i]) * ac
|
|
|
|
vel_values[i] += force
|
|
|
|
vel_values[i] *= trenie
|
|
|
|
y_values[i] += vel_values[i]
|
|
|
|
base_y = ym - (ym - y1) * y_scale * y_values[i]
|
|
|
|
base_y_last = ym - (ym - y1) * y_scale * y_values[i - 1]
|
|
|
|
|
|
|
|
DrawLine(prev_x, base_y_last, cur_x, base_y, 1.0 * line_width_scale, style)
|
|
|
|
|
|
|
|
if vel_graph_on:
|
|
|
|
base_y = ym - (ym - y1) * vel_graph_scale * vel_values[i]
|
|
|
|
base_y_last = ym - (ym - y1) * vel_graph_scale * vel_values[i - 1]
|
|
|
|
|
|
|
|
DrawLine(prev_x, base_y_last, cur_x, base_y, 1.0, style_vel)
|
|
|
|
|
|
|
|
if force_graph_on:
|
|
|
|
base_y = ym - (ym - y1) * force_graph_scale * force
|
|
|
|
base_y_last = ym - (ym - y1) * force_graph_scale * force
|
|
|
|
|
|
|
|
DrawLine(prev_x, base_y_last, cur_x, base_y, 1.0, style_force)
|
|
|
|
|
|
|
|
|
|
|
|
prev_x = cur_x
|
|
|
|
|
|
|
|
def recalculate_event():
|
|
|
|
recalculate()
|
|
|
|
|
|
|
|
def reload_event(event):
|
|
|
|
reload()
|
|
|
|
|
|
|
|
reload()
|
|
|
|
|
|
|
|
document["reload"].bind("click", reload_event)
|
|
|
|
|
|
|
|
game_loop = window.setInterval(recalculate_event, 1000/20)
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
|
|
|
|
</html>
|