finishing the project
BIN
assets/fonts/Rubik.ttf
Normal file
BIN
assets/fonts/RubikItalic.ttf
Normal file
BIN
assets/images/ico/favicon.ico
Normal file
After Width: | Height: | Size: 5.6 KiB |
1
assets/images/svg/arrow_down.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 10l5 5 5-5z"/></svg>
|
After Width: | Height: | Size: 171 B |
1
assets/images/svg/download.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
|
After Width: | Height: | Size: 198 B |
1
assets/images/svg/pause.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
After Width: | Height: | Size: 188 B |
1
assets/images/svg/play_arrow.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M8 5v14l11-7z"/></svg>
|
After Width: | Height: | Size: 170 B |
1
assets/images/svg/restart.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><g><path d="M12,5V2L8,6l4,4V7c3.31,0,6,2.69,6,6c0,2.97-2.17,5.43-5,5.91v2.02c3.95-0.49,7-3.85,7-7.93C20,8.58,16.42,5,12,5z"/><path d="M6,13c0-1.65,0.67-3.15,1.76-4.24L6.34,7.34C4.9,8.79,4,10.79,4,13c0,4.08,3.05,7.44,7,7.93v-2.02 C8.17,18.43,6,15.97,6,13z"/></g></g></svg>
|
After Width: | Height: | Size: 456 B |
1
assets/images/svg/upload.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></svg>
|
After Width: | Height: | Size: 194 B |
73
index.html
@ -3,14 +3,83 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Document</title>
|
<title>Particle Sandbox</title>
|
||||||
<script src="script/main.js"></script>
|
<script defer src="script/main.js"></script>
|
||||||
<link rel="stylesheet" href="style/main.css" />
|
<link rel="stylesheet" href="style/main.css" />
|
||||||
|
<link
|
||||||
|
rel="shortcut icon"
|
||||||
|
href="assets/images/ico/favicon.ico"
|
||||||
|
type="image/x-icon"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
<section class="gui">
|
<section class="gui">
|
||||||
|
<div class="content">
|
||||||
|
<div class="head">
|
||||||
|
<h1 class="title">Particle Sandbox</h1>
|
||||||
|
<div class="head-buttons">
|
||||||
|
<button class="play-pause">
|
||||||
|
<img
|
||||||
|
src="/assets/images/svg/play_arrow.svg"
|
||||||
|
alt="play-pause"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button class="reset">
|
||||||
|
<img
|
||||||
|
src="/assets/images/svg/restart.svg"
|
||||||
|
alt="reset"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button class="minimize">
|
||||||
|
<img
|
||||||
|
src="/assets/images/svg/arrow_down.svg"
|
||||||
|
alt="minimize"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="category">
|
||||||
|
<div class="category-head">
|
||||||
|
<button class="add particle">+</button>
|
||||||
<h2 class="label">Particles</h2>
|
<h2 class="label">Particles</h2>
|
||||||
|
</div>
|
||||||
|
<div class="particle-containers"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="category-head">
|
||||||
|
<button class="add interaction">+</button>
|
||||||
|
<h2 class="label">Interactions</h2>
|
||||||
|
</div>
|
||||||
|
<div class="interaction-containers"></div>
|
||||||
|
</div>
|
||||||
|
<div class="down-buttons">
|
||||||
|
<button class="save">
|
||||||
|
<img
|
||||||
|
src="assets/images/svg/upload.svg"
|
||||||
|
alt="upload"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
/>
|
||||||
|
<p>Export this scene</p>
|
||||||
|
</button>
|
||||||
|
<button class="load">
|
||||||
|
<img
|
||||||
|
src="assets/images/svg/download.svg"
|
||||||
|
alt="download"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
/>
|
||||||
|
<p>Import a scene</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
449
script/main.js
@ -1,4 +1,24 @@
|
|||||||
window.onload = function () {
|
var _a, _b, _c, _d, _e, _f, _g;
|
||||||
|
var SLIDER_MIN = 50;
|
||||||
|
var SLIDER_MAX = 1000;
|
||||||
|
var SLIDER_STEP = 50;
|
||||||
|
var SLIDER_DEFAULT = 350;
|
||||||
|
var MAX_INTERACTION_NUMBER = 20;
|
||||||
|
var MIN_INTENSITY = 1;
|
||||||
|
var MAX_INTENSITY = 1000;
|
||||||
|
var DEFAULT_INTENSITY = 100;
|
||||||
|
var INTENSITY_STEP = 1;
|
||||||
|
var INTENSITY_DIVIDER = 10000;
|
||||||
|
var DISTANCE_MIN = 10;
|
||||||
|
var DISTANCE_MAX = 500;
|
||||||
|
var DISTANCE_STEP = 10;
|
||||||
|
var DISTANCE_DEFAULT = 200;
|
||||||
|
var Group1 = 0;
|
||||||
|
var Group2 = 1;
|
||||||
|
var canvas = document.querySelector("canvas");
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
var Particle = /** @class */ (function () {
|
var Particle = /** @class */ (function () {
|
||||||
function Particle(x, y, color, radius) {
|
function Particle(x, y, color, radius) {
|
||||||
this.vx = 0;
|
this.vx = 0;
|
||||||
@ -8,24 +28,236 @@ window.onload = function () {
|
|||||||
this.color = color;
|
this.color = color;
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
}
|
}
|
||||||
Particle.prototype.draw = function () {
|
Particle.prototype.draw = function (ctx) {
|
||||||
ctx.beginPath();
|
ctx.fillStyle = ColorsHex[this.color];
|
||||||
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
ctx.fillRect(this.x, this.y, this.radius, this.radius);
|
||||||
ctx.fillStyle = this.color;
|
|
||||||
ctx.fill();
|
|
||||||
};
|
};
|
||||||
Particle.DEFAULT_RADIUS = 2.5;
|
Particle.DEFAULT_RADIUS = 5;
|
||||||
return Particle;
|
return Particle;
|
||||||
}());
|
}());
|
||||||
var canvas = document.querySelector("canvas");
|
var paused = true;
|
||||||
var ctx = canvas.getContext("2d");
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
window.addEventListener("resize", function () {
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
});
|
|
||||||
var particles = [];
|
var particles = [];
|
||||||
|
var interactions = [];
|
||||||
|
var ColorsHex = Object.freeze({
|
||||||
|
red: "#f91d4d",
|
||||||
|
green: "#0db342",
|
||||||
|
blue: "#4a4ad7",
|
||||||
|
yellow: "#f0e246",
|
||||||
|
magenta: "#d742d7",
|
||||||
|
cyan: "#42cedb",
|
||||||
|
});
|
||||||
|
var colors = {
|
||||||
|
red: null,
|
||||||
|
green: null,
|
||||||
|
blue: null,
|
||||||
|
yellow: null,
|
||||||
|
magenta: null,
|
||||||
|
cyan: null,
|
||||||
|
};
|
||||||
|
function reset() {
|
||||||
|
particles.forEach(function (particle) {
|
||||||
|
particle.x = randomInt(0, canvas.width);
|
||||||
|
particle.y = randomInt(0, canvas.height);
|
||||||
|
});
|
||||||
|
paused = true;
|
||||||
|
var img = document.querySelector(".play-pause img");
|
||||||
|
img.src = "/assets/images/svg/play_arrow.svg";
|
||||||
|
}
|
||||||
|
function addColor(color, autoGroup) {
|
||||||
|
if (autoGroup === void 0) { autoGroup = true; }
|
||||||
|
if (autoGroup)
|
||||||
|
colors[color] = makeGroup(350, color, { radius: Particle.DEFAULT_RADIUS });
|
||||||
|
var container = document.querySelector(".particle-containers");
|
||||||
|
var particle = document.createElement("div");
|
||||||
|
particle.classList.add("particle-container");
|
||||||
|
particle.classList.add(color);
|
||||||
|
var head = document.createElement("div");
|
||||||
|
head.classList.add("container-head");
|
||||||
|
head.innerHTML = "<div class=\"color\"></div><h3 class=\"sublabel\">".concat(color, "</h3><button class=\"close particle\">\u2A2F</button>");
|
||||||
|
var close = head.querySelector(".close");
|
||||||
|
close.addEventListener("click", function () {
|
||||||
|
particles = particles.filter(function (p) { return p.color !== color; });
|
||||||
|
colors[color] = null;
|
||||||
|
container.removeChild(particle);
|
||||||
|
interactions = interactions.filter(function (itn) {
|
||||||
|
var _a, _b, _c, _d;
|
||||||
|
if (itn.group1[0].color === color || itn.group2[0].color === color) {
|
||||||
|
(_d = (_c = (_b = (_a = document
|
||||||
|
.querySelector(".interaction-container .colors .color.".concat(color))) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.parentElement) === null || _c === void 0 ? void 0 : _c.parentElement) === null || _d === void 0 ? void 0 : _d.remove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var particleSetting = document.createElement("h4");
|
||||||
|
particleSetting.classList.add("setting");
|
||||||
|
particleSetting.innerHTML = "Particle number : <span class=\"setting-value\">".concat(SLIDER_DEFAULT.toLocaleString("en-US"), "</span>");
|
||||||
|
var range = document.createElement("div");
|
||||||
|
range.classList.add("range");
|
||||||
|
range.innerHTML = "<p class=\"value\">".concat(SLIDER_MIN.toLocaleString("en-US"), "</p><input class=\"range-content\" type=\"range\" min=\"").concat(SLIDER_MIN, "\" max=\"").concat(SLIDER_MAX, "\" step=\"").concat(SLIDER_STEP, "\" value=\"").concat(SLIDER_DEFAULT, "\"><p class=\"value\">").concat(SLIDER_MAX.toLocaleString("en-US"), "</p>");
|
||||||
|
var input = range.querySelector("input");
|
||||||
|
input.addEventListener("input", function () {
|
||||||
|
var value = parseInt(input.value);
|
||||||
|
particleSetting.innerHTML = "Particle number : <span class=\"setting-value\"Z>".concat(value.toLocaleString("en-US"), "</span>");
|
||||||
|
particles = particles.filter(function (p) { return p.color !== color; });
|
||||||
|
colors[color] = makeGroup(value, color, {
|
||||||
|
radius: Particle.DEFAULT_RADIUS,
|
||||||
|
});
|
||||||
|
interactions = interactions.map(function (itn) {
|
||||||
|
if (itn.group1[0].color === color) {
|
||||||
|
itn.group1 = colors[color];
|
||||||
|
}
|
||||||
|
else if (itn.group2[0].color === color) {
|
||||||
|
itn.group2 = colors[color];
|
||||||
|
}
|
||||||
|
return itn;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
particle.appendChild(head);
|
||||||
|
particle.appendChild(particleSetting);
|
||||||
|
particle.appendChild(range);
|
||||||
|
container.appendChild(particle);
|
||||||
|
}
|
||||||
|
function colorSelector(selected, group, interaction) {
|
||||||
|
var selector = document.createElement("div");
|
||||||
|
selector.classList.add("color-selector");
|
||||||
|
var selectedColor = document.createElement("button");
|
||||||
|
selectedColor.classList.add("color");
|
||||||
|
selectedColor.classList.add(selected);
|
||||||
|
var colorList = document.createElement("div");
|
||||||
|
colorList.classList.add("color-list");
|
||||||
|
selectedColor.addEventListener("click", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (colorList.classList.contains("show")) {
|
||||||
|
colorList.classList.remove("show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var colorButtons = document.querySelectorAll(".color-list");
|
||||||
|
colorButtons.forEach(function (colorButton) {
|
||||||
|
if (colorButton.classList.contains("show")) {
|
||||||
|
colorButton.classList.remove("show");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
colorList.classList.add("show");
|
||||||
|
colorList.innerHTML = "";
|
||||||
|
var _loop_1 = function (color) {
|
||||||
|
if (!colors[color])
|
||||||
|
return "continue";
|
||||||
|
var colorButton = document.createElement("button");
|
||||||
|
colorButton.classList.add("color");
|
||||||
|
colorButton.classList.add(color);
|
||||||
|
colorButton.addEventListener("click", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
selectedColor.classList.remove(selected);
|
||||||
|
selected = color;
|
||||||
|
selectedColor.classList.add(selected);
|
||||||
|
colorList.classList.remove("show");
|
||||||
|
switch (group) {
|
||||||
|
case Group1:
|
||||||
|
interaction.group1 = colors[color];
|
||||||
|
break;
|
||||||
|
case Group2:
|
||||||
|
interaction.group2 = colors[color];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
colorList.appendChild(colorButton);
|
||||||
|
};
|
||||||
|
for (var color in colors) {
|
||||||
|
_loop_1(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selector.appendChild(selectedColor);
|
||||||
|
selector.appendChild(colorList);
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
function addInteraction(interaction) {
|
||||||
|
interactions.push(interaction);
|
||||||
|
var isAttractionForce = true;
|
||||||
|
var container = document.querySelector(".interaction-containers");
|
||||||
|
var interactionContainer = document.createElement("div");
|
||||||
|
interactionContainer.classList.add("interaction-container");
|
||||||
|
var colors = document.createElement("div");
|
||||||
|
colors.classList.add("colors");
|
||||||
|
var color1 = colorSelector(interaction.group1[0].color, 0, interaction);
|
||||||
|
var color2 = colorSelector(interaction.group2[0].color, 1, interaction);
|
||||||
|
var liaison = document.createElement("h3");
|
||||||
|
liaison.classList.add("setting");
|
||||||
|
if (isAttractionForce) {
|
||||||
|
liaison.textContent = "is attracted to";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
liaison.textContent = "is repulsed by";
|
||||||
|
}
|
||||||
|
var close = document.createElement("button");
|
||||||
|
close.classList.add("close");
|
||||||
|
close.classList.add("interaction");
|
||||||
|
close.textContent = "⨯";
|
||||||
|
colors.appendChild(color1);
|
||||||
|
colors.appendChild(liaison);
|
||||||
|
colors.appendChild(color2);
|
||||||
|
colors.appendChild(close);
|
||||||
|
var forceContainer = document.createElement("div");
|
||||||
|
forceContainer.classList.add("force-container");
|
||||||
|
forceContainer.innerHTML = "<h4 class=\"setting\">Force Type</h4><div class=\"force-types\"><button class=\"force-type selected\">Attraction</button><button class=\"force-type\">Repulsion</button></div>";
|
||||||
|
var forceTypes = forceContainer.querySelectorAll(".force-type");
|
||||||
|
forceTypes.forEach(function (forceType) {
|
||||||
|
forceType.addEventListener("click", function () {
|
||||||
|
forceTypes.forEach(function (ft) { return ft.classList.remove("selected"); });
|
||||||
|
forceType.classList.add("selected");
|
||||||
|
isAttractionForce = forceType.textContent === "Attraction";
|
||||||
|
if (isAttractionForce) {
|
||||||
|
liaison.textContent = "is attracted to";
|
||||||
|
interaction.options.g = -Math.abs(interaction.options.g);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
liaison.textContent = "is repulsed by";
|
||||||
|
interaction.options.g = Math.abs(interaction.options.g);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var intensityContainer = document.createElement("h4");
|
||||||
|
intensityContainer.classList.add("setting");
|
||||||
|
intensityContainer.innerHTML = "Intensity : <span class=\"setting-value\">".concat(DEFAULT_INTENSITY.toLocaleString("en-US"), "</span>");
|
||||||
|
var intensityRange = document.createElement("div");
|
||||||
|
intensityRange.classList.add("range");
|
||||||
|
intensityRange.innerHTML = "<p class=\"value\">".concat(MIN_INTENSITY.toLocaleString("en-US"), "</p><input class=\"range-content\" type=\"range\" min=\"").concat(MIN_INTENSITY, "\" max=\"").concat(MAX_INTENSITY, "\" step=\"").concat(INTENSITY_STEP, "\" value=\"").concat(DEFAULT_INTENSITY, "\"><p class=\"value\">").concat(MAX_INTENSITY.toLocaleString("en-US"), "</p>");
|
||||||
|
var intensityInput = intensityRange.querySelector("input");
|
||||||
|
intensityInput.addEventListener("input", function () {
|
||||||
|
var value = parseInt(intensityInput.value);
|
||||||
|
intensityContainer.innerHTML = "Intensity : <span class=\"setting-value\">".concat(value.toLocaleString("en-US"), "</span>");
|
||||||
|
if (isAttractionForce) {
|
||||||
|
interaction.options.g = -value / INTENSITY_DIVIDER;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
interaction.options.g = value / INTENSITY_DIVIDER;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var distanceContainer = document.createElement("h4");
|
||||||
|
distanceContainer.classList.add("setting");
|
||||||
|
distanceContainer.innerHTML = "Distance : <span class=\"setting-value\">".concat(DISTANCE_DEFAULT.toLocaleString("en-US"), "</span>");
|
||||||
|
var distanceRange = document.createElement("div");
|
||||||
|
distanceRange.classList.add("range");
|
||||||
|
distanceRange.innerHTML = "<p class=\"value\">".concat(DISTANCE_MIN.toLocaleString("en-US"), "</p><input class=\"range-content\" type=\"range\" min=\"").concat(DISTANCE_MIN, "\" max=\"").concat(DISTANCE_MAX, "\" step=\"").concat(DISTANCE_STEP, "\" value=\"").concat(DISTANCE_DEFAULT, "\"><p class=\"value\">").concat(DISTANCE_MAX.toLocaleString("en-US"), "</p>");
|
||||||
|
var distanceInput = distanceRange.querySelector("input");
|
||||||
|
distanceInput.addEventListener("input", function () {
|
||||||
|
var value = parseInt(distanceInput.value);
|
||||||
|
distanceContainer.innerHTML = "Distance : <span class=\"setting-value\">".concat(value.toLocaleString("en-US"), "</span>");
|
||||||
|
interaction.options.distance = value;
|
||||||
|
});
|
||||||
|
close.addEventListener("click", function () {
|
||||||
|
interactions = interactions.filter(function (itn) { return itn !== interaction; });
|
||||||
|
container.removeChild(interactionContainer);
|
||||||
|
});
|
||||||
|
interactionContainer.appendChild(colors);
|
||||||
|
interactionContainer.appendChild(forceContainer);
|
||||||
|
interactionContainer.appendChild(intensityContainer);
|
||||||
|
interactionContainer.appendChild(intensityRange);
|
||||||
|
interactionContainer.appendChild(distanceContainer);
|
||||||
|
interactionContainer.appendChild(distanceRange);
|
||||||
|
container.appendChild(interactionContainer);
|
||||||
|
}
|
||||||
function randomInt(min, max) {
|
function randomInt(min, max) {
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
}
|
}
|
||||||
@ -40,11 +272,10 @@ window.onload = function () {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
function interaction(group1, group2, options) {
|
function interaction(group1, group2, options) {
|
||||||
var _a, _b, _c;
|
var _a, _b;
|
||||||
if (options === void 0) { options = {}; }
|
if (options === void 0) { options = {}; }
|
||||||
options.g = (_a = options.g) !== null && _a !== void 0 ? _a : 0.1;
|
var g = (_a = options.g) !== null && _a !== void 0 ? _a : 0.1;
|
||||||
options.maxDistance = (_b = options.maxDistance) !== null && _b !== void 0 ? _b : 100;
|
var distance = (_b = options.distance) !== null && _b !== void 0 ? _b : 100;
|
||||||
options.minDistance = (_c = options.minDistance) !== null && _c !== void 0 ? _c : 0;
|
|
||||||
for (var i = 0; i < group1.length; i++) {
|
for (var i = 0; i < group1.length; i++) {
|
||||||
var fx = 0;
|
var fx = 0;
|
||||||
var fy = 0;
|
var fy = 0;
|
||||||
@ -54,33 +285,181 @@ window.onload = function () {
|
|||||||
var dx = a.x - b.x;
|
var dx = a.x - b.x;
|
||||||
var dy = a.y - b.y;
|
var dy = a.y - b.y;
|
||||||
var d = Math.sqrt(dx * dx + dy * dy);
|
var d = Math.sqrt(dx * dx + dy * dy);
|
||||||
if (d > 0 && d < options.maxDistance && d > options.minDistance) {
|
if (d > 0 && d < distance) {
|
||||||
var F = options.g / d;
|
var F = g / d;
|
||||||
fx += F * dx;
|
fx += F * dx;
|
||||||
fy += F * dy;
|
fy += F * dy;
|
||||||
}
|
}
|
||||||
a.vx = (a.vx + fx) * 0.5;
|
a.vx = (a.vx + fx) * 0.5;
|
||||||
a.vy = (a.vy + fy) * 0.5;
|
a.vy = (a.vy + fy) * 0.5;
|
||||||
a.x += a.vx * 0.005;
|
a.x += a.vx * 0.01;
|
||||||
a.y += a.vy * 0.005;
|
a.y += a.vy * 0.01;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var blue = makeGroup(5000, "#0000ff", { radius: 1 });
|
(_a = document.querySelector(".add.particle")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", function () {
|
||||||
var red = makeGroup(1000, "#ff0000", { radius: 2.5 });
|
var color = null;
|
||||||
var green = makeGroup(50, "#00ff00", { radius: 4 });
|
for (var c in colors) {
|
||||||
|
if (!colors[c]) {
|
||||||
|
color = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (color)
|
||||||
|
addColor(color);
|
||||||
|
});
|
||||||
|
(_b = document.querySelector(".add.interaction")) === null || _b === void 0 ? void 0 : _b.addEventListener("click", function () {
|
||||||
|
if (interactions.length >= MAX_INTERACTION_NUMBER)
|
||||||
|
return;
|
||||||
|
var color1 = null;
|
||||||
|
var color2 = null;
|
||||||
|
for (var c in colors) {
|
||||||
|
if (colors[c]) {
|
||||||
|
color1 = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!color1)
|
||||||
|
return;
|
||||||
|
for (var c in colors) {
|
||||||
|
if (colors[c] && c !== color1) {
|
||||||
|
color2 = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!color2)
|
||||||
|
color2 = color1;
|
||||||
|
var group1 = colors[color1];
|
||||||
|
var group2 = colors[color2];
|
||||||
|
if (!group1 || !group2)
|
||||||
|
return;
|
||||||
|
var intensity = DEFAULT_INTENSITY;
|
||||||
|
var distance = 100;
|
||||||
|
var options = {
|
||||||
|
distance: distance,
|
||||||
|
g: intensity / INTENSITY_DIVIDER,
|
||||||
|
};
|
||||||
|
var interaction = {
|
||||||
|
group1: group1,
|
||||||
|
group2: group2,
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
|
addInteraction(interaction);
|
||||||
|
});
|
||||||
|
(_c = document.querySelector(".play-pause")) === null || _c === void 0 ? void 0 : _c.addEventListener("click", function () {
|
||||||
|
paused = !paused;
|
||||||
|
var img = document.querySelector(".play-pause img");
|
||||||
|
img.src = paused
|
||||||
|
? "/assets/images/svg/play_arrow.svg"
|
||||||
|
: "/assets/images/svg/pause.svg";
|
||||||
|
});
|
||||||
|
(_d = document.querySelector(".reset")) === null || _d === void 0 ? void 0 : _d.addEventListener("click", function () {
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
(_e = document.querySelector(".save")) === null || _e === void 0 ? void 0 : _e.addEventListener("click", function () {
|
||||||
|
var element = document.createElement("a");
|
||||||
|
element.style.display = "none";
|
||||||
|
element.download = "ps".concat(Date.now(), ".json");
|
||||||
|
element.setAttribute("href", "data:text/plain;charset=utf-8," +
|
||||||
|
encodeURIComponent(JSON.stringify([
|
||||||
|
interactions.map(function (itn) { return ({
|
||||||
|
group1: itn.group1[0].color,
|
||||||
|
group2: itn.group2[0].color,
|
||||||
|
options: itn.options,
|
||||||
|
}); }),
|
||||||
|
Object.entries(colors)
|
||||||
|
.filter(function (_a) {
|
||||||
|
var _ = _a[0], v = _a[1];
|
||||||
|
return v;
|
||||||
|
})
|
||||||
|
.map(function (_a) {
|
||||||
|
var k = _a[0], _ = _a[1];
|
||||||
|
return ({
|
||||||
|
color: k,
|
||||||
|
number: colors[k].length,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
])));
|
||||||
|
element.click();
|
||||||
|
element.remove();
|
||||||
|
});
|
||||||
|
(_f = document.querySelector(".load")) === null || _f === void 0 ? void 0 : _f.addEventListener("click", function () {
|
||||||
|
var element = document.createElement("input");
|
||||||
|
element.style.display = "none";
|
||||||
|
element.type = "file";
|
||||||
|
element.accept = ".json";
|
||||||
|
element.addEventListener("change", function (e) {
|
||||||
|
var file = e.target.files[0];
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.addEventListener("load", function (e) {
|
||||||
|
var _a = JSON.parse(e.target.result), itn = _a[0], groups = _a[1];
|
||||||
|
interactions = [];
|
||||||
|
particles = [];
|
||||||
|
var particleContainer = document.querySelector(".particle-containers");
|
||||||
|
var interactionContainer = document.querySelector(".interaction-containers");
|
||||||
|
particleContainer.innerHTML = "";
|
||||||
|
interactionContainer.innerHTML = "";
|
||||||
|
for (var _i = 0, groups_1 = groups; _i < groups_1.length; _i++) {
|
||||||
|
var group = groups_1[_i];
|
||||||
|
addColor(group.color, false);
|
||||||
|
colors[group.color] = makeGroup(group.number, group.color, {
|
||||||
|
radius: Particle.DEFAULT_RADIUS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (var _b = 0, itn_1 = itn; _b < itn_1.length; _b++) {
|
||||||
|
var it = itn_1[_b];
|
||||||
|
var group1 = colors[it.group1];
|
||||||
|
var group2 = colors[it.group2];
|
||||||
|
if (!group1 || !group2)
|
||||||
|
continue;
|
||||||
|
addInteraction({
|
||||||
|
group1: group1,
|
||||||
|
group2: group2,
|
||||||
|
options: it.options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
element.click();
|
||||||
|
element.remove();
|
||||||
|
});
|
||||||
|
(_g = document.querySelector(".minimize")) === null || _g === void 0 ? void 0 : _g.addEventListener("click", function () {
|
||||||
|
var gui = document.querySelector(".gui .content");
|
||||||
|
gui.classList.toggle("minimized");
|
||||||
|
});
|
||||||
|
window.addEventListener("resize", function () {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
window.addEventListener("click", function () {
|
||||||
|
document.querySelectorAll(".color-list").forEach(function (colorList) {
|
||||||
|
if (colorList.classList.contains("show")) {
|
||||||
|
colorList.classList.remove("show");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
addColor("red");
|
||||||
|
addColor("green");
|
||||||
|
addInteraction({
|
||||||
|
group1: colors.red,
|
||||||
|
group2: colors.green,
|
||||||
|
options: {
|
||||||
|
distance: DISTANCE_DEFAULT,
|
||||||
|
g: -DEFAULT_INTENSITY / INTENSITY_DIVIDER,
|
||||||
|
},
|
||||||
|
});
|
||||||
function animate() {
|
function animate() {
|
||||||
interaction(red, red, { g: -0.05, maxDistance: 250 });
|
if (!paused) {
|
||||||
interaction(green, red, { g: -0.1, maxDistance: 500 });
|
interactions.forEach(function (itn) {
|
||||||
interaction(red, green, { g: -0.05, maxDistance: 250 });
|
return interaction(itn.group1, itn.group2, itn.options);
|
||||||
interaction(blue, red, { g: -0.4, maxDistance: 250 });
|
});
|
||||||
|
}
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
ctx.fillStyle = "#000000";
|
ctx.fillStyle = "#000000";
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
particles.forEach(function (particle) {
|
particles.forEach(function (particle) { return particle.draw(ctx); });
|
||||||
particle.draw();
|
return requestAnimationFrame(animate);
|
||||||
});
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
}
|
}
|
||||||
animate();
|
animate();
|
||||||
};
|
|
||||||
|