diff --git a/ensemble-of-pendulums/Dockfile b/ensemble-of-pendulums/Dockfile
new file mode 100644
index 0000000..dacbb27
--- /dev/null
+++ b/ensemble-of-pendulums/Dockfile
@@ -0,0 +1,3 @@
+FROM nginx
+
+COPY . /usr/share/nginx/html
diff --git a/ensemble-of-pendulums/css/style.css b/ensemble-of-pendulums/css/style.css
new file mode 100644
index 0000000..9310a4a
--- /dev/null
+++ b/ensemble-of-pendulums/css/style.css
@@ -0,0 +1,483 @@
+@font-face {
+ font-family: "Times New Roman";
+ src: url(../fonts/Times-New-Roman.ttf);
+}
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font: inherit;
+ vertical-align: baseline;
+ outline: none;
+ font-family: "SF Pro Display", Arial;
+}
+
+html {
+ height: 101%;
+ font-family: "SF Pro Display";
+ font-size: 10px;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+
+ol,
+ul {
+ list-style: none;
+}
+
+blockquote,
+q {
+ quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: "";
+ content: none;
+}
+
+strong {
+ font-weight: bold;
+}
+
+input {
+ outline: none;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+img {
+ border: 0;
+ max-width: 100%;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+*,
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+button {
+ border: none;
+ cursor: pointer;
+}
+
+.container {
+ max-width: 1170px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 1rem;
+}
+
+.header {
+ background: #1e1e1e;
+ padding: 2rem 0;
+}
+
+.navigation {
+ display: flex;
+ justify-content: space-between;
+ font-style: normal;
+ font-weight: 700;
+ font-size: 2rem;
+ font-family: "SF Pro Display";
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+.navigation .authentication {
+ color: #ffffff;
+ display: flex;
+ font-family: "SF Pro Display";
+}
+.navigation .authentication .log-in {
+ margin-right: 8rem;
+}
+
+.flex {
+ display: flex;
+ align-items: center;
+}
+
+.main {
+ margin-top: 4.5rem;
+ font-size: 2.5rem;
+ color: #000000;
+}
+.main .main-content {
+ display: flex;
+}
+.main .main-content .options h2 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+}
+.main .main-content .options .visual {
+ margin-top: 15rem;
+}
+.main .main-content .options .visual h3 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+}
+.main .main-content .manager {
+ margin-top: 7.5rem;
+ color: #ffffff;
+}
+.main .main-content .manager h3 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+}
+.main .main-content .manager .button {
+ font-style: normal;
+ font-weight: 700;
+ font-size: 2.5rem;
+ line-height: 3rem;
+ padding: 0.9rem 2rem;
+ border-radius: 1rem;
+ filter: drop-shadow(4px 4px 10px rgba(0, 0, 0, 0.2));
+}
+.main .main-content .manager .button:not(:last-child) {
+ margin-right: 2.7rem;
+}
+.main .main-content .manager .graph-go {
+ background: #1aff00;
+ color: #ffffff;
+}
+.main .main-content .manager .graph-stop {
+ background: #fbff00;
+ color: #ffffff;
+}
+.main .main-content .manager .graph-reset {
+ color: #000000;
+ background: #ff0000;
+}
+.main .main-content .graph {
+ order: 1;
+ height: 100%;
+ width: 100%;
+}
+.main .main-content .graph h2 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+}
+.main .main-content .graph .img {
+ margin-top: 2rem;
+ border: 1rem solid rgb(102, 0, 255);
+ width: 100%;
+ height: 100%;
+}
+
+.parameter-item h4 {
+ min-width: 20rem;
+}
+
+.amplitude {
+ margin-top: 1rem;
+}
+
+input,
+output {
+ display: inline-block;
+ vertical-align: middle;
+ font-size: 1em;
+ font-family: Arial, sans-serif;
+}
+
+output {
+ background: #393939;
+ border-radius: 3px;
+ color: #fff;
+}
+
+input[type=number] {
+ width: 6rem;
+ padding: 0.5rem 0.5rem 0.5rem 0.7rem;
+ border-radius: 3px;
+ border: none;
+ background: #e4e4e4;
+}
+
+input[type=range] {
+ margin-left: 1.5rem;
+ -webkit-appearance: none;
+ margin-right: 15px;
+ width: 30rem;
+ height: 0.4rem;
+ background: rgba(255, 255, 255, 0.6);
+ border-radius: 5px;
+ background-image: linear-gradient(#d3ffd6, #393939);
+}
+
+/* Input Thumb */
+input[type=range]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #030063;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #ff0000;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type=range]::-moz-range-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #393939;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #555;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type=range]::-ms-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #393939;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #555;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type=range]::-webkit-slider-thumb:hover {
+ background: #393939;
+}
+
+input[type=range]::-moz-range-thumb:hover {
+ background: #393939;
+}
+
+input[type=range]::-ms-thumb:hover {
+ background: #393939;
+}
+
+/* Input Track */
+input[type=range]::-webkit-slider-runnable-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input[type=range]::-moz-range-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input[type=range]::-ms-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ /* display: none; <- Crashes Chrome on hover */
+ -webkit-appearance: none;
+ margin-left: -5rem;
+ padding-left: -5rem;
+ margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+}
+
+@media screen and (max-width: 1170px) {
+ .container {
+ padding: 0 1rem;
+ }
+ .main-content {
+ flex-direction: column;
+ }
+ .graph {
+ margin-top: 2rem;
+ }
+ .parameter-item {
+ justify-content: center;
+ }
+ h2,
+h3 {
+ text-align: center;
+ }
+ .buttons {
+ display: block;
+ text-align: center;
+ }
+}
+@media screen and (max-width: 440px) {
+ .navigation {
+ flex-direction: column;
+ align-items: center;
+ }
+ .authentication {
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .log-in {
+ margin-bottom: 2rem;
+ margin-left: 8rem;
+ }
+ .buttons {
+ display: flex;
+ flex-direction: column;
+ }
+ .button {
+ margin-top: 2rem;
+ margin-right: 0;
+ margin-left: 2.7rem;
+ }
+ .graph-reset {
+ margin-right: 2.7rem;
+ }
+ .parameter-item {
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .parameter-item input {
+ margin-top: 2rem;
+ }
+ h4 {
+ text-align: center;
+ }
+}
+
+.manager {
+ position: absolute;
+ bottom: 100px;
+}
+.parameter-graph {
+ position: relative;
+ top: 100px;
+}
\ No newline at end of file
diff --git a/ensemble-of-pendulums/docker-compose.yml b/ensemble-of-pendulums/docker-compose.yml
new file mode 100644
index 0000000..28257a1
--- /dev/null
+++ b/ensemble-of-pendulums/docker-compose.yml
@@ -0,0 +1,9 @@
+version: '3'
+services:
+ web:
+ image: img-static-site-example
+ build: .
+ container_name: my-static-site
+ restart: always
+ ports:
+ - "8080:80"
diff --git a/ensemble-of-pendulums/fonts/Times-New-Roman.ttf b/ensemble-of-pendulums/fonts/Times-New-Roman.ttf
new file mode 100644
index 0000000..55f734a
Binary files /dev/null and b/ensemble-of-pendulums/fonts/Times-New-Roman.ttf differ
diff --git a/ensemble-of-pendulums/index.html b/ensemble-of-pendulums/index.html
new file mode 100644
index 0000000..5646760
--- /dev/null
+++ b/ensemble-of-pendulums/index.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
График
+ Текущее время: None
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Время синхронизации: None
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ensemble-of-pendulums/js/main.js b/ensemble-of-pendulums/js/main.js
new file mode 100644
index 0000000..e89fcf1
--- /dev/null
+++ b/ensemble-of-pendulums/js/main.js
@@ -0,0 +1,445 @@
+const g = 9.81;
+const EPS = 1E-1;
+
+function round_to_2(number) {
+ return Math.round((number + Number.EPSILON) * 100) / 100;
+}
+
+class App {
+ constructor() {
+ var flag = 0;
+ this.canvas = document.getElementById("canvas");
+ this.context = this.canvas.getContext("2d");
+
+ this.canvas.width = canvas.offsetWidth;
+ this.canvas.height = canvas.offsetHeight;
+ this.lengthE1 = document.getElementById("_length");
+ this.frequencyE1 = document.getElementById("delta_length");
+ this.amplitudeE1 = document.getElementById("amplitude");
+ this.countE1 = document.getElementById("count");
+ this.sizeE1 = document.getElementById("_size");
+
+ this.range_lengthE1 = document.getElementById("range_length");
+ this.range_frequencyE1 = document.getElementById("range_delta_length");
+ this.range_amplitudeE1 = document.getElementById("range_amplitude");
+ this.range_countE1 = document.getElementById("range_count");
+ this.range_sizeE1 = document.getElementById("range_size");
+
+ this.interval = 30
+ this.run = false;
+ this.button_go = document.getElementById("go");
+ this.button_stop = document.getElementById("stop");
+ this.button_reset = document.getElementById("reset")
+ this.is_grid = document.getElementById("grid");
+
+ var field_t_sync = document.getElementById("t_sync");
+ field_t_sync.innerHTML = "Время синхронизации: " + round_to_2(this.period_sync());
+
+ this.draw_line();
+
+ this.button_reset.addEventListener('click', () => {
+ this.is_grid.checked = true;
+ this.range_lengthE1.value = 1;
+ this.range_frequencyE1.value = 0.1;
+ this.range_amplitudeE1.value = 100;
+ this.range_countE1.value = 7;
+ this.range_sizeE1.value = 10;
+ this.lengthE1.value = 1;
+ this.frequencyE1.value = 0.1;
+ this.countE1.value = 7;
+ this.sizeE1.value = 10;
+ this.amplitudeE1.value = 100;
+ });
+
+ [this.button_go, this.button_reset].forEach(element => {
+ clearInterval(this.timer);
+ element.addEventListener("click", event => {
+ var field_t_sync = document.getElementById("t_sync");
+ field_t_sync.innerHTML = "Время синхронизации: " + round_to_2(this.period_sync());
+
+ clearInterval(this.timer);
+ if (!flag && this.validateData()) {
+ const data = this.getData();
+
+ if (this.Ball) {
+ this.Ball.reset();
+ data.time = this.Ball.time;
+ }
+
+ this.Ball = new Ball(50, 50, data.size, data.length, data.frequecy, data.amplitude);
+
+ if (isFinite(data.time)) {
+ this.Ball.time = data.time;
+ }
+
+ this.timer = setInterval(() => this.redraw(), this.interval);
+ }
+
+ this.run = 1;
+ flag = 0;
+ });
+ });
+
+ [this.countE1,
+ this.sizeE1,
+ this.lengthE1,
+ this.frequencyE1,
+ this.amplitudeE1,
+ this.range_amplitudeE1,
+ this.range_countE1,
+ this.range_frequencyE1,
+ this.range_lengthE1,
+ this.range_sizeE1
+ ].forEach(element => {
+ clearInterval(this.timer);
+ element.addEventListener("change", event => {
+ clearInterval(this.timer);
+ if (!flag && this.validateData()) {
+ const data = this.getData();
+ if (this.Ball) {
+ data.time = this.Ball.time;
+ }
+
+ var field_t_sync = document.getElementById("t_sync");
+ field_t_sync.innerHTML = "Время синхронизации: " + round_to_2(this.period_sync());
+ this.Ball = new Ball(50, 50, data.size, data.length, data.frequecy, data.amplitude);
+
+ if (isFinite(data.time)) {
+ this.Ball.time = data.time;
+ }
+
+ this.timer = setInterval(() => this.redraw(), this.interval);
+ }
+
+ this.run = 1;
+ flag = 0;
+ });
+ });
+
+ this.button_stop.addEventListener('click', () => {
+ if (this.run) {
+ this.enableInputFields();
+ clearInterval(this.timer);
+ }
+ flag = 0;
+ this.run = 0;
+ });
+ }
+
+ draw_line() {
+ this.canvas.width = canvas.offsetWidth;
+ this.canvas.height = canvas.offsetHeight;
+ this.context.beginPath();
+ this.context.setLineDash([5, 3]);
+ this.context.moveTo(0, this.canvas.height / 2);
+ this.context.lineTo(this.canvas.width, this.canvas.height / 2);
+ this.context.strokeStyle = "black";
+ this.context.lineWidth = "2";
+ this.context.stroke();
+ this.context.closePath();
+ }
+
+ clear() {
+ this.canvas.width = canvas.offsetWidth;
+ this.canvas.height = canvas.offsetHeight;
+
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ redraw() {
+ this.canvas.width = canvas.offsetWidth;
+ this.canvas.height = canvas.offsetHeight;
+
+ this.clear();
+ this.draw_line();
+ this.Ball.draw(this.context, this.interval);
+ }
+
+ getData() {
+ const length = parseFloat(this.lengthE1.value);
+ const delta_length = parseFloat(this.frequencyE1.value);
+ const amplitude = parseFloat(this.amplitudeE1.value);
+ const count = parseFloat(this.countE1.value);
+ const size = parseFloat(this.sizeE1.value);
+
+ if (isFinite(length) && isFinite(delta_length) && isFinite(amplitude) && isFinite(count) && isFinite(size))
+ return {
+ length: length,
+ delta_length: delta_length,
+ amplitude: amplitude,
+ count: count,
+ size: size
+ };
+ else
+ return null;
+ }
+
+ validateData() {
+ const data = this.getData();
+ if (data === null) {
+ alert("Одно или несколько полей заполнены непраивльно или не заполнены совсем!");
+ return false;
+ }
+ else {
+ if (data.length < 0.1 || data.length > 10) {
+ alert("Длина должена быть в пределах от 0.1 до 10!");
+ return false;
+ }
+ if (data.frequecy < 0.1 || data.frequecy > 1) {
+ alert("Длина должена быть в пределах от 0.01 до 1!");
+ return false;
+ }
+ if (data.amplitude < 1 || data.amplitude > 200) {
+ alert("Амплитуда должна быть в пределах от 1 до 200!");
+ return false;
+ }
+ if (data.count < 1 || data.count > 100) {
+ alert("Количество должно быть в пределах от 1 до 100!");
+ return false;
+ }
+ if (data.size < 1 || data.size > 100) {
+ alert("Размер должен быть в пределах от 1 до 100!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ enableInputFields() {
+ this.lengthE1.removeAttribute('disabled');
+ this.frequencyE1.removeAttribute('disabled');
+ this.amplitudeE1.removeAttribute('disabled');
+ this.countE1.removeAttribute('disabled');
+ this.sizeE1.removeAttribute('disabled');
+
+ this.range_lengthE1.removeAttribute('disabled');
+ this.range_frequencyE1.removeAttribute('disabled');
+ this.range_amplitudeE1.removeAttribute('disabled');
+ this.range_countE1.removeAttribute('disabled');
+ this.range_sizeE1.removeAttribute('disabled');
+ }
+
+ disableInputFields() {
+ this.lengthE1.setAttribute('disabled', 'disabled');
+ this.frequencyE1.setAttribute('disabled', 'disabled');
+ this.amplitudeE1.setAttribute('disabled', 'disabled');
+ this.countE1.setAttribute('disabled', 'disabled');
+ this.sizeE1.setAttribute('disabled', 'disabled');
+
+ this.range_lengthE1.setAttribute('disabled', 'disabled');
+ this.range_frequencyE1.setAttribute('disabled', 'disabled');
+ this.range_amplitudeE1.setAttribute('disabled', 'disabled');
+ this.range_countE1.setAttribute('disabled', 'disabled');
+ this.range_sizeE1.setAttribute('disabled', 'disabled');
+ }
+
+ gcd(a, b) {
+ if (!b) {
+ return a;
+ }
+
+ return gcd(b, a % b);
+ }
+
+ LCM(arr) {
+ function gcd(a, b) {
+ if (b === 0) return a;
+ return gcd(b, a % b);
+ }
+
+ let res = arr[0];
+
+ for (let i = 1; i < arr.length; i++) {
+ res = (res * arr[i]) / gcd(res, arr[i]);
+ }
+
+ return res;
+ }
+
+ period(length) {
+ const T = 2 * Math.PI * Math.sqrt(length / g);
+ return T;
+ }
+
+ frequecy(length) {
+ var T = this.period(length);
+ return 1 / T;
+ }
+
+ period_sync_old() {
+ const data = this.getData();
+ var temp = 0;
+
+ for (let i = 0; i < data.count; i++) {
+ temp += Math.pow(data.length + i * data.delta_length, 2);
+ }
+
+ var T = 2 * Math.PI * Math.sqrt(1 / (g * data.count)) * temp;
+
+ return T;
+ }
+
+ period_sync_2() {
+ const data = this.getData();
+
+ var f1 = 1 / this.period(data.length);
+ var f2 = 1 / this.period(data.length + (data.count - 1) * data.delta_length);
+
+ var T = 1 / (f1 - f2);
+
+ return T;
+ }
+
+ period_sync_1() {
+ const data = this.getData();
+ let arr = new Array();
+
+ for (let i = 0; i < data.count; i++) {
+ arr.push(Math.round(this.period(data.length + i * data.delta_length)));
+ }
+
+ var T = this.LCM(arr);
+
+ return T;
+ }
+
+ period_sync_3() {
+ const data = this.getData();
+ var temp1 = data.length;
+ var temp2 = data.length;
+
+ for (let i = 1; i < data.count; i++) {
+ temp1 *= this.period(data.length + i * data.delta_length);
+ temp2 -= this.period(data.length + i * data.delta_length);
+ }
+
+ var T = -(temp1 / temp2);
+
+ return T;
+ }
+
+ period_sync_4() {
+ const data = this.getData();
+ var temp = this.frequecy(data.length);
+
+ for (let i = 1; i < data.count; i++) {
+ temp += this.frequecy(data.length + i * data.delta_length);
+ }
+
+ var T = (2 * Math.PI) / (Math.sqrt((1 / data.count) * temp));
+
+ return T;
+ }
+
+ period_sync() {
+ var time = 0;
+ const data = this.getData();
+ var nado = false;
+
+ var zero = data.amplitude;
+
+ while (!nado) {
+ time += 30 / 100;
+ let arr = [];
+
+ for (let i = 0; i < data.count; i++) {
+ arr.push(data.amplitude * Math.sin(Math.PI / 2 + time * (data.length + i * data.delta_length) / (2 * Math.PI)));
+ }
+
+ nado = this.is_equal_arr(arr, zero);
+ }
+ return time / 10;
+ }
+
+ is_equal_arr(arr, zero) {
+ var eq = true;
+ for (let i = 0; i < arr.length; i++) {
+ if (Math.abs(arr[i] - zero) > EPS) {
+ eq = false;
+ break;
+ }
+ }
+ return eq;
+ }
+
+}
+// пососный js
+class Ball {
+ constructor(x0, y0, size, length, delta_length, amplitude) {
+ this.x = x0;
+ this.y = y0;
+ this.size = size;
+ this.time = 0;
+ this.length = length;
+ this.amplitude = amplitude;
+ this.delta_length = delta_length;
+ this.count = parseFloat(document.getElementById("count").value);
+ }
+
+ grid(canvas, context, span) {
+ var w = canvas.width - 1;
+ var h = canvas.height - 1;
+ context.strokeStyle = "rgba(100,150,185,0.5)";
+ for (var x = -0.5; x < w; x += span) context.strokeRect(x, 0, 0.1, h);
+ for (var y = -0.5; y < h; y += span) context.strokeRect(0, y, w, 0.1);
+ //return cnv.toDataURL();
+ }
+
+ reset() {
+ this.time = 0;
+ }
+
+ getTime() {
+ return this.time / 1000;
+ }
+
+ drawBall(context) {
+ const gradient = context.createRadialGradient(this.x, this.y, this.size, this.x - 2, this.y - 4, 2);
+
+ gradient.addColorStop(0, '#333');
+ gradient.addColorStop(1, '#8b00ff');
+
+ context.fillStyle = gradient;
+ context.beginPath();
+ context.arc(this.x, this.y, this.size, 0, Math.PI * 2, true);
+ context.fill();
+ }
+
+ calcY() {
+ return this.amplitude * Math.sin(Math.PI / 2 + this.time * this.length / (2 * Math.PI));
+ }
+
+ draw(context, interval) {
+ this.time += interval / 100;
+ this.between_distance = document.getElementById("canvas").width / (this.count + 1);
+ this.x = this.between_distance;
+
+ const HEIGHT = parseFloat(document.getElementById("canvas").height) / 2;
+ const LENGTH = parseFloat(document.getElementById("_length").value);
+ const DELTA_LENGTH = parseFloat(document.getElementById("delta_length").value);
+
+ if (document.getElementById('grid').checked) {
+ this.grid(document.getElementById("canvas"), context, 23);
+ }
+
+ for (let i = 0; i < this.count; i++) {
+ this.length = LENGTH + i * DELTA_LENGTH;
+ this.y = HEIGHT + this.calcY();
+
+ this.drawBall(context);
+ this.x += this.between_distance;
+ }
+
+ var field_time = document.getElementById("time");
+ field_time.innerHTML = "Время: " + round_to_2(this.time / 10);
+ }
+}
+
+window.onload = () => {
+ var options = document.getElementById("id_options");
+ var options_height = options.offsetHeight;
+
+ document.getElementById('id_graph').style.height = options_height + 'px';
+
+ new App();
+}
diff --git a/ensemble-of-pendulums/sass/style.scss b/ensemble-of-pendulums/sass/style.scss
new file mode 100644
index 0000000..82ad6db
--- /dev/null
+++ b/ensemble-of-pendulums/sass/style.scss
@@ -0,0 +1,478 @@
+@font-face {
+ font-family: "SF Pro Display";
+ src: url(../fonts/SF-Pro-Display-Bold.ttf);
+}
+
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font: inherit;
+ vertical-align: baseline;
+ outline: none;
+ font-family: "SF Pro Display", Arial;
+}
+html {
+ height: 101%;
+ font-family: "SF Pro Display";
+ font-size: 10px;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+ol,
+ul {
+ list-style: none;
+}
+
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: "";
+ content: none;
+}
+strong {
+ font-weight: bold;
+}
+
+input {
+ outline: none;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+img {
+ border: 0;
+ max-width: 100%;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+*,
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+button {
+ border: none;
+ cursor: pointer;
+}
+
+.container {
+ max-width: 1170px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 1rem;
+}
+
+.header {
+ background: #1e1e1e;
+ padding: 2rem 0;
+}
+
+.navigation {
+ display: flex;
+ justify-content: space-between;
+ font-style: normal;
+ font-weight: 700;
+ font-size: 2rem;
+ font-family: "SF Pro Display";
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ color: rgba(255, 255, 255, 0.5);
+
+ .authentication {
+ color: #ffffff;
+ display: flex;
+ font-family: "SF Pro Display";
+ .log-in {
+ margin-right: 8rem;
+ }
+ }
+}
+
+.flex {
+ display: flex;
+ align-items: center;
+}
+
+.main {
+ margin-top: 4.5rem;
+ font-size: 2.5rem;
+ color: #000000;
+ .main-content {
+ display: flex;
+ .options {
+ h2 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+ }
+ .visual {
+ margin-top: 7.5rem;
+ h3 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+ }
+ }
+ }
+ .manager {
+ margin-top: 7.5rem;
+ color: #ffffff;
+ h3 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ margin-bottom: 2rem;
+ }
+ .button {
+ font-style: normal;
+ font-weight: 700;
+ font-size: 2.5rem;
+ line-height: 3rem;
+ padding: 0.9rem 2rem;
+ border-radius: 1rem;
+ filter: drop-shadow(4px 4px 10px rgba(0, 0, 0, 0.2));
+ }
+
+ .button:not(:last-child) {
+ margin-right: 2.7rem;
+ }
+
+ .graph-go {
+ background: #00a843;
+ color: #ffffff;
+ }
+ .graph-stop {
+ background: #e83535;
+ color: #ffffff;
+ }
+ .graph-reset {
+ color: #000000;
+ background: #d9d9d9;
+ }
+ }
+ .graph {
+ order: 1;
+ height: 100%;
+ width: 100%;
+ h2 {
+ font-size: 2rem;
+ color: rgba(0, 0, 0, 0.4);
+ text-transform: uppercase;
+ line-height: 2.4rem;
+ font-style: normal;
+ font-weight: 700;
+ }
+ .img {
+ margin-top: 2rem;
+ border: 0.1rem solid black;
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+}
+
+.parameter-item {
+ h4 {
+ min-width: 20rem;
+ }
+}
+
+.amplitude {
+ margin-top: 1rem;
+}
+
+input,
+output {
+ display: inline-block;
+ vertical-align: middle;
+ font-size: 1em;
+ font-family: Arial, sans-serif;
+}
+
+output {
+ background: #393939;
+ border-radius: 3px;
+ color: #fff;
+}
+
+input[type="number"] {
+ width: 6rem;
+ padding: 0.5rem 2rem 0.5rem 0.7rem;
+ border-radius: 3px;
+ border: none;
+ background: #e4e4e4;
+}
+
+input[type="range"] {
+ margin-left: 1.5rem;
+ -webkit-appearance: none;
+ margin-right: 15px;
+ width: 30rem;
+ height: 0.4rem;
+ background: rgba(255, 255, 255, 0.6);
+ border-radius: 5px;
+ background-image: linear-gradient(#393939, #393939);
+}
+
+/* Input Thumb */
+input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #393939;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #555;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type="range"]::-moz-range-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #393939;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #555;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type="range"]::-ms-thumb {
+ -webkit-appearance: none;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background: #393939;
+ cursor: ew-resize;
+ box-shadow: 0 0 2px 0 #555;
+ transition: background 0.3s ease-in-out;
+}
+
+input[type="range"]::-webkit-slider-thumb:hover {
+ background: #393939;
+}
+
+input[type="range"]::-moz-range-thumb:hover {
+ background: #393939;
+}
+
+input[type="range"]::-ms-thumb:hover {
+ background: #393939;
+}
+
+/* Input Track */
+input[type="range"]::-webkit-slider-runnable-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input[type="range"]::-moz-range-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input[type="range"]::-ms-track {
+ -webkit-appearance: none;
+ box-shadow: none;
+ border: none;
+ background: transparent;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ /* display: none; <- Crashes Chrome on hover */
+ -webkit-appearance: none;
+ margin-left: -5rem;
+ padding-left: -5rem;
+ margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+}
+
+@media screen and (max-width: 1170px) {
+ .container {
+ padding: 0 1rem;
+ }
+ .main-content {
+ flex-direction: column;
+ }
+ .graph {
+ margin-top: 2rem;
+ }
+ .parameter-item {
+ justify-content: center;
+ }
+ h2,
+ h3 {
+ text-align: center;
+ }
+ .buttons {
+ display: block;
+ text-align: center;
+ }
+}
+
+@media screen and (max-width: 440px) {
+ .navigation {
+ flex-direction: column;
+ align-items: center;
+ }
+ .authentication {
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .log-in {
+ margin-bottom: 2rem;
+ margin-left: 8rem;
+ }
+ .buttons {
+ display: flex;
+ flex-direction: column;
+ }
+ .button{
+ margin-top: 2rem;
+ margin-right: 0;
+ margin-left: 2.7rem;
+ }
+ .graph-reset {
+ margin-right: 2.7rem;
+ }
+ .parameter-item {
+ input {
+ margin-top: 2rem;
+ }
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ h4 {
+ text-align: center;
+ }
+}
\ No newline at end of file