commit
27a0e31cbd
11 changed files with 1086 additions and 0 deletions
@ -0,0 +1,2 @@ |
||||
.idea |
||||
|
@ -0,0 +1,6 @@ |
||||
{ |
||||
"display_name": "Physics", |
||||
"info_text": "A small physical interface for creating objects with interacting physical properties", |
||||
"visible": false, |
||||
"tags": ["Simulation", "Maths"] |
||||
} |
@ -0,0 +1,88 @@ |
||||
#color_picker{ |
||||
width: 300px; |
||||
height: 25%; |
||||
margin: 20px; |
||||
margin-top: 50px; |
||||
border: 5px solid #000; |
||||
background-color: #000; |
||||
-webkit-user-select: none; |
||||
-moz-user-select: none; |
||||
-ms-user-select: none; |
||||
user-select: none; |
||||
position: relative; |
||||
} |
||||
#color_picker_numeric{ |
||||
width: 80%; |
||||
padding: 5%; |
||||
margin: 5%; |
||||
background-color: #888; |
||||
border-radius: 10px; |
||||
overflow: hidden; |
||||
} |
||||
.color_picker_rgb{ |
||||
float: left; |
||||
width: 22%; |
||||
height: 35px; |
||||
font-size: 25px; |
||||
color: #000; |
||||
} |
||||
.color_picker_rgb:nth-child(1){ |
||||
margin-right: 10%; |
||||
margin-left: 3%; |
||||
background-color: #F00; |
||||
|
||||
} |
||||
.color_picker_rgb:nth-child(2){ |
||||
background-color: #0F0; |
||||
} |
||||
.color_picker_rgb:nth-child(3){ |
||||
margin-left: 10%; |
||||
background-color: #00F; |
||||
color: #FFF; |
||||
} |
||||
#color_picker_hex{ |
||||
width: 50%; |
||||
height: 30px; |
||||
font-size: 25px; |
||||
margin: 10% 25% 0 25%; |
||||
} |
||||
#saturation{ |
||||
position: relative; |
||||
width: calc(100% - 33px); |
||||
height: 100%; |
||||
background: linear-gradient(to right, #FFF 0%, #F00 100%); |
||||
float: left; |
||||
margin-right: 6px; |
||||
} |
||||
#value { |
||||
width: 100%; |
||||
height: 100%; |
||||
background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%); |
||||
} |
||||
#sb_picker{ |
||||
border: 2px solid; |
||||
border-color: #FFF; |
||||
position: absolute; |
||||
width: 14px; |
||||
height: 14px; |
||||
border-radius: 10px; |
||||
bottom: 50px; |
||||
left: 50px; |
||||
box-sizing: border-box; |
||||
z-index: 10; |
||||
} |
||||
#hue { |
||||
width: 27px; |
||||
height: 100%; |
||||
position: relative; |
||||
float: left; |
||||
background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%); |
||||
} |
||||
#hue_picker { |
||||
position: absolute; |
||||
background: #000; |
||||
border-bottom: 1px solid #000; |
||||
top: 0; |
||||
width: 27px; |
||||
height: 2px; |
||||
} |
@ -0,0 +1,47 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js" type="text/javascript"></script> |
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js" type="text/javascript"></script> |
||||
<script src="scripts/lib/benjo_library.js" type="text/javascript"></script> |
||||
<script src="scripts/events.js" type="text/javascript"></script> |
||||
<script src="scripts/sketch.js" type="text/javascript"></script> |
||||
<script src="scripts/ball.js" type="text/javascript"></script> |
||||
<script src="scripts/attraction.js" type="text/javascript"></script> |
||||
<link href="styles.css" rel="stylesheet"> |
||||
<link href="color_picker_styles.css" rel="stylesheet"> |
||||
<title>Physics</title> |
||||
</head> |
||||
<body> |
||||
<div id="canvasHolder"></div> |
||||
<div id="mainInterface"> |
||||
<select id="itemSelector"> |
||||
<option value="ball">Ball</option> |
||||
<option value="attractor">Magnet</option> |
||||
</select> |
||||
<input id="radiusRange" max="75" min="5" oninput="updateRadius(this);" type="range" value="20"/> |
||||
<center> |
||||
<span>Radius in Pixeln: </span> |
||||
<input id="radiusInput" max="75" min="5" oninput="updateRadius(this)" type="number" value="20"> |
||||
</center> |
||||
<div id="color_picker"> |
||||
<div id="saturation"> |
||||
<div id="value"> |
||||
<div id="sb_picker"></div> |
||||
</div> |
||||
</div> |
||||
<div id="hue"> |
||||
<div id="hue_picker"></div> |
||||
</div> |
||||
</div> |
||||
<div id="color_picker_numeric"> |
||||
<input class="color_picker_rgb" max="255" min="0" onchange="colorPicker.updateFromRGB()" oninput="colorPicker.updateFromRGB()" type="number"/> |
||||
<input class="color_picker_rgb" max="255" min="0" onchange="colorPicker.updateFromRGB()" oninput="colorPicker.updateFromRGB()" type="number"/> |
||||
<input class="color_picker_rgb" max="255" min="0" onchange="colorPicker.updateFromRGB()" oninput="colorPicker.updateFromRGB()" type="number"/> |
||||
<input id="color_picker_hex" onchange="colorPicker.updateFromHEX(this, false)" style="background-color: #F00;" style="background-color: #F00;" type="text"/> |
||||
</div> |
||||
<div id="slot"></div> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,26 @@ |
||||
class Attraction{ |
||||
|
||||
constructor(x, y, r, c){ |
||||
this.r = r; |
||||
this.pos = createVector(x, y); |
||||
this.c = c; |
||||
} |
||||
|
||||
show(){ |
||||
noStroke(); |
||||
fill(this.c); |
||||
ellipse(this.pos.x, this.pos.y, this.r * 2); |
||||
} |
||||
|
||||
grabbed(){ |
||||
this.pos.x = mouseX + viewPort.x; |
||||
this.pos.y = mouseY + viewPort.y; |
||||
} |
||||
|
||||
checkGrab(){ |
||||
if (dist(this.pos.x, this.pos.y, mouseX + viewPort.x, mouseY + viewPort.y) < this.r && !this.isGrabbed){ |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,69 @@ |
||||
class Ball{ |
||||
|
||||
constructor(x, y, r, c){ |
||||
this.maxAcc = 0.5; |
||||
this.r = r; |
||||
this.pos = createVector(x, y); |
||||
this.vel = createVector(0, 0); |
||||
this.acc = createVector(0, 0); |
||||
this.c = c; |
||||
} |
||||
|
||||
attracted(){ |
||||
let att = createVector(0, 0), G = 50, g = 3; |
||||
for (let a of attractions){ |
||||
let distV = p5.Vector.sub(a.pos, this.pos); |
||||
distV.setMag(G * this.r * a.r / pow(this.pos.dist(a.pos), 2)); |
||||
att.add(distV.div(this.r)); |
||||
} |
||||
if (gravity) att.y += g; |
||||
this.acc = att; |
||||
this.acc.limit(this.maxAcc); |
||||
} |
||||
|
||||
move(){ |
||||
this.vel.add(this.acc); |
||||
this.pos.add(this.vel); |
||||
} |
||||
|
||||
show(){ |
||||
noStroke(); |
||||
fill(this.c); |
||||
ellipse(this.pos.x, this.pos.y, this.r * 2); |
||||
} |
||||
|
||||
debugInformation(){ |
||||
let velX = p5.Vector.mult(this.vel, 10).x; |
||||
let velY = p5.Vector.mult(this.vel, 10).y; |
||||
let accX = velX + p5.Vector.mult(this.acc, 100).x; |
||||
let accY = velY + p5.Vector.mult(this.acc, 100).y; |
||||
push(); |
||||
translate(this.pos.x, this.pos.y); |
||||
strokeWeight(2); |
||||
stroke(255, 0, 0); |
||||
fill(255, 0, 0); |
||||
line(0, 0, velX, velY); |
||||
translate(velX, velY); |
||||
rotate(this.vel.heading()); |
||||
beginShape(); |
||||
vertex(0, 0); |
||||
vertex(-10, 10); |
||||
vertex(-10, -10); |
||||
endShape(); |
||||
rotate(-this.vel.heading()); |
||||
translate(-velX, -velY); |
||||
stroke(0, 255, 0); |
||||
fill(0, 255, 0); |
||||
line(velX, velY, accX, accY); |
||||
translate(accX, accY); |
||||
rotate(this.acc.heading()); |
||||
beginShape(); |
||||
vertex(0, 0); |
||||
vertex(-10, 10); |
||||
vertex(-10, -10); |
||||
endShape(); |
||||
rotate(-this.acc.heading()); |
||||
translate(-accX, -accY); |
||||
pop(); |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
function keyPressed(){ |
||||
if (userIsWriting) return true; |
||||
switch(keyCode){ |
||||
case 73: |
||||
debug = !debug; |
||||
break; |
||||
} |
||||
if (keyCode != 116) return false; |
||||
} |
||||
|
||||
function mouseDragged(){ |
||||
colorPicker.mouseDragged(); |
||||
for (let a of attractions){ |
||||
if (a.isGrabbed) a.grabbed(); |
||||
} |
||||
} |
||||
|
||||
function mousePressed(){ |
||||
colorPicker.mousePressed(); |
||||
for (let a of attractions){ |
||||
if (dist(a.pos.x, a.pos.y, mouseX + viewPort.x, mouseY + viewPort.y) < a.r){ |
||||
a.isGrabbed = true; |
||||
$("#defaultCanvas0").css({ |
||||
"cursor": "-webkit-grabbing", |
||||
"cursor": "grabbing" |
||||
}); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function mouseReleased(){ |
||||
colorPicker.mouseReleased(); |
||||
for (let a of attractions){ |
||||
if (a.isGrabbed){ |
||||
a.isGrabbed = false; |
||||
$("#defaultCanvas0").css({ |
||||
"cursor": "auto" |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,623 @@ |
||||
var TOP = 1; |
||||
var RIGHT = 2 |
||||
var BOTTOM = 3; |
||||
var LEFT = 4; |
||||
var TOP_RIGHT = 5; |
||||
var BOTTOM_RIGHT = 6; |
||||
var BOTTOM_LEFT = 7; |
||||
var TOP_LEFT = 8; |
||||
|
||||
var wWidth = window.innerWidth; |
||||
var wHeight = window.innerHeight; |
||||
var oldWHeight; |
||||
var oldWWidth; |
||||
|
||||
let frameRateArray = []; |
||||
|
||||
function updateVars(){ |
||||
oldWWidth = wWidth; |
||||
oldWHeight = wHeight; |
||||
wWidth = window.innerWidth; |
||||
wHeight = window.innerHeight; |
||||
} |
||||
|
||||
function collisionDetection(obj0, obj1){ |
||||
|
||||
var sp = strokePadding; |
||||
if (sp == null) sp = 0; |
||||
|
||||
if (obj0.isEllipse && obj1.isRectangle){ |
||||
|
||||
//Ball
|
||||
var b = obj0; |
||||
|
||||
//Rectangle
|
||||
var r = obj1; |
||||
|
||||
for (var i = 0; i < TWO_PI; i += PI / 32){ |
||||
|
||||
/* Check every borderpoint of the ball beginning |
||||
at the top in clock direction up to top again */ |
||||
|
||||
// Ball Center X
|
||||
var bcx = b.x; |
||||
|
||||
// Ball Center Y
|
||||
var bcy = b.y; |
||||
|
||||
// Ball Border X
|
||||
var bbx = b.x + sin(i) * b.r; |
||||
|
||||
// Ball Border Y inverted because Y = 0 is the TOP of the screen
|
||||
var bby = b.y - cos(i) * b.r; |
||||
|
||||
// Rectangle Width
|
||||
var rW = r.width + 2 * sp; |
||||
|
||||
// Rectangle Height
|
||||
var rH = r.height + 2 * sp; |
||||
|
||||
// Rectangle Border X
|
||||
var rX = r.x - sp; |
||||
|
||||
// Rectangle Border Y
|
||||
var rY = r.y - sp; |
||||
|
||||
// Objects touch
|
||||
if (bbx > rX && bbx < rX + rW |
||||
&& bby > rY && bby < rY + rH){ |
||||
|
||||
// STRAIGHT FACES //
|
||||
|
||||
//Top/Bottom touch
|
||||
if (bcx > rX && bcx < rX + rW){
|
||||
|
||||
//Top touch
|
||||
if (b.v.y > 0) return {isTouching: true, location: TOP}; |
||||
|
||||
//Bottom touch
|
||||
if (b.v.y < 0) return {isTouching: true, location: BOTTOM}; |
||||
} |
||||
|
||||
//Left/Right touch
|
||||
if (bcy > rY && bcy < rY + rH){ |
||||
|
||||
//Left touch
|
||||
if (b.v.x > 0) return {isTouching: true, location: LEFT}; |
||||
|
||||
//Right touch
|
||||
if (b.v.x < 0) return {isTouching: true, location: RIGHT}; |
||||
|
||||
} |
||||
|
||||
// CORNERS //
|
||||
|
||||
// BOTTOM Left/Right
|
||||
if (i > 0 && i <= PI / 2) return {isTouching: true, location: BOTTOM_LEFT}; |
||||
|
||||
//LEFT Bottom/Top
|
||||
if (i > PI / 2 && i <= PI) return {isTouching: true, location: TOP_LEFT}; |
||||
|
||||
//TOP Left/Right
|
||||
if (i > PI && i <= PI + PI / 2) return {isTouching: true, location: TOP_RIGHT}; |
||||
|
||||
//RIGHT Bottom/Top
|
||||
if (i > PI + PI / 2 && i <= TWO_PI) return {isTouching: true, location: BOTTOM_RIGHT}; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
if (obj0.isEllipse && obj1.isEllipse){ |
||||
|
||||
//Ball 1
|
||||
var b1 = obj0; |
||||
|
||||
//Ball 2
|
||||
var b2 = obj1; |
||||
|
||||
//Balls are close to each other
|
||||
if (b1.x + b1.r > b2.x - b2.r |
||||
&& b1.x - b1.r < b2.x + b2.r |
||||
&& b1.y + b1.r > b2.y - b2.r |
||||
&& b1.y - b1.r < b2.y + b2.r){ |
||||
|
||||
var distance = sqrt(pow(b1.x - b2.x, 2) + pow(b1.y - b2.y, 2)); |
||||
|
||||
if (distance < b1.r + b2.r) return {isTouching: true}; |
||||
} |
||||
} |
||||
return {isTouching: false, location: 0}; |
||||
} |
||||
|
||||
function performCollision(obj0, obj1, collision){ |
||||
if (obj0.isEllipse){ |
||||
|
||||
var ball = obj0; |
||||
|
||||
//Ball collides with frameborder
|
||||
if (obj1.isFrameborder){ |
||||
switch (collision.location){ |
||||
case BOTTOM: |
||||
ball.v.y *= -1; |
||||
break; |
||||
case LEFT: |
||||
case RIGHT: |
||||
ball.v.x *= -1; |
||||
break; |
||||
} |
||||
if (testMode && collision.location == TOP) ball.v.y *= -1; |
||||
ball.move(); |
||||
} |
||||
|
||||
//Ball collides with any brick
|
||||
if (obj1.isBrick){ |
||||
switch (collision.location){ |
||||
case TOP: |
||||
case BOTTOM: |
||||
ball.v.y *= -1; |
||||
ball.move(); |
||||
return; |
||||
case LEFT: |
||||
case RIGHT: |
||||
ball.v.x *= -1; |
||||
ball.move(); |
||||
return; |
||||
case TOP_LEFT: |
||||
var cornerX = obj1.x; |
||||
var cornerY = obj1.y; |
||||
break; |
||||
case TOP_RIGHT: |
||||
var cornerX = obj1.x + obj1.width; |
||||
var cornerY = obj1.y; |
||||
break; |
||||
case BOTTOM_LEFT: |
||||
var cornerX = obj1.x; |
||||
var cornerY = obj1.y + obj1.height; |
||||
break; |
||||
case BOTTOM_RIGHT: |
||||
var cornerX = obj1.x + obj1.width; |
||||
var cornerY = obj1.y + obj1.height; |
||||
break; |
||||
} |
||||
|
||||
var nx = ball.x - cornerX; |
||||
var ny = ball.y - cornerY; |
||||
var length = sqrt(nx * nx + ny * ny); |
||||
nx /= length; |
||||
ny /= length; |
||||
|
||||
var projection = ball.v.x * nx + ball.v.y * ny; |
||||
ball.v.x = ball.v.x - 2 * projection * nx; |
||||
ball.v.y = ball.v.y - 2 * projection * ny; |
||||
|
||||
ball.move(); |
||||
|
||||
} |
||||
|
||||
//Ball collides with paddleboard
|
||||
if (obj1.isPaddle){ |
||||
switch (collision.location){ |
||||
case TOP: |
||||
case TOP_LEFT: |
||||
case TOP_RIGHT: |
||||
ball.v.x = ball.calcVelocityX(obj1, ball.v.x); |
||||
ball.v.y = -ball.calcVelocityY(); |
||||
ball.move(); |
||||
return; |
||||
case LEFT: |
||||
case RIGHT: |
||||
ball.v.x *= -1; |
||||
ball.move(); |
||||
return; |
||||
case BOTTOM_LEFT: |
||||
var cornerX = obj1.x; |
||||
var cornerY = obj1.y + obj1.height; |
||||
break; |
||||
case BOTTOM_RIGHT: |
||||
var cornerX = obj1.x + obj1.width; |
||||
var cornerY = obj1.y + obj1.height; |
||||
break; |
||||
} |
||||
var nx = ball.x - cornerX; |
||||
var ny = ball.y - cornerY; |
||||
var length = sqrt(nx * nx + ny * ny); |
||||
nx /= length; |
||||
ny /= length; |
||||
|
||||
var projection = ball.v.x * nx + ball.v.y * ny; |
||||
ball.v.x = ball.v.x - 2 * projection * nx; |
||||
ball.v.y = ball.v.y - 2 * projection * ny; |
||||
|
||||
ball.move(); |
||||
} |
||||
|
||||
//Ball collides with other ball
|
||||
if (obj1.isEllipse){ |
||||
|
||||
//Ball 1
|
||||
var b1 = obj0; |
||||
|
||||
//Ball 2
|
||||
var b2 = obj1; |
||||
|
||||
//Set mass equal to radius of each ball
|
||||
b1.mass = b1.r; |
||||
b2.mass = b2.r; |
||||
|
||||
//Colliding angle of ball 1 to ball 2 using arc tan of both x and y differences
|
||||
var collisionAngle = atan2((b2.y - b1.y), (b2.x - b1.x)); |
||||
|
||||
//Converting directions of velocity vector of balls into angles
|
||||
var d1 = atan2(b1.v.y, b1.v.x); |
||||
var d2 = atan2(b2.v.y, b2.v.x); |
||||
|
||||
//Ignoring mass effects new velocites are simply magnitude multiplied with value of angle differences
|
||||
var newXspeed1 = b1.v.mag * cos(d1 - collisionAngle); |
||||
var newYspeed1 = b1.v.mag * sin(d1 - collisionAngle); |
||||
var newXspeed2 = b2.v.mag * cos(d2 - collisionAngle); |
||||
var newYspeed2 = b2.v.mag * sin(d2 - collisionAngle); |
||||
|
||||
//According to the principle of linear momentum, kinetic energy stays the same after collision, so velocities are now related to masses
|
||||
var finalXspeed1 = ((b1.mass - b2.mass) * newXspeed1 + b2.mass * 2 * newXspeed2) / (b1.mass + b2.mass); |
||||
var finalYspeed1 = newYspeed1; |
||||
var finalXspeed2 = (b1.mass * 2 * newXspeed1 + (b2.mass - b1.mass) * newXspeed2) / (b1.mass + b2.mass); |
||||
var finalYspeed2 = newYspeed2; |
||||
|
||||
//Values of collisionAngle
|
||||
var cosAngle = cos(collisionAngle); |
||||
var sinAngle = sin(collisionAngle); |
||||
|
||||
//To also keep velocites relative to pure collisionAngle, subtract sin*x from cos*x and add sin*y to cos*y because coordSystem has y = 0 on the top
|
||||
var u1x = cosAngle * finalXspeed1 - sinAngle * finalYspeed1; |
||||
var u1y = sinAngle * finalXspeed1 + cosAngle * finalYspeed1; |
||||
var u2x = cosAngle * finalXspeed2 - sinAngle * finalYspeed2; |
||||
var u2y = sinAngle * finalXspeed2 + cosAngle * finalYspeed2; |
||||
|
||||
//Set new velocities to both balls
|
||||
b1.v.x = u1x; |
||||
b1.v.y = u1y; |
||||
b2.v.x = u2x; |
||||
b2.v.y = u2y; |
||||
|
||||
//Update magnitude
|
||||
b1.v.mag = sqrt(pow(b1.v.x, 2) + pow(b1.v.y, 2)); |
||||
b2.v.mag = sqrt(pow(b2.v.x, 2) + pow(b2.v.y, 2)); |
||||
|
||||
|
||||
//Move balls one vx/vy forward to avoid double inverting collision detection
|
||||
b1.x += b1.v.x; |
||||
b1.y += b1.v.y; |
||||
b2.x += b2.v.x; |
||||
b2.y += b2.v.y; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
function toTimeString(time, hoursWanted){ |
||||
|
||||
var time = floor(time / 10); |
||||
|
||||
var hs = String(floor(time % 100)); |
||||
var fs = String(floor((time / 100) % 60)); |
||||
|
||||
if (hoursWanted){ |
||||
var min = String(floor(((time / 100) / 60) % 60)); |
||||
var hr = String(floor(((time / 100) / 60) / 60)); |
||||
|
||||
if (hs.length < 2) hs = "0" + hs; |
||||
if (fs.length < 2) fs = "0" + fs; |
||||
if (min.length < 2) min = "0" + min; |
||||
if (hr.length < 2) hr = "0" + hr; |
||||
|
||||
var timeString = hr + ":" + min + ":" + fs + ":" + hs; |
||||
} else { |
||||
var min = String(floor(((time / 100) / 60) % 60)); |
||||
|
||||
if (hs.length < 2) hs = "0" + hs; |
||||
if (fs.length < 2) fs = "0" + fs; |
||||
if (min.length < 2) min = "0" + min; |
||||
|
||||
var timeString = min + ":" + fs + ":" + hs; |
||||
} |
||||
|
||||
|
||||
|
||||
return timeString; |
||||
} |
||||
|
||||
|
||||
function setCookie(name, value, years){ |
||||
var expires = ""; |
||||
if (years){ |
||||
var date = new Date(); |
||||
date.setTime(date.getTime() + (years * 365 * 24 * 60 * 60 * 1000)); |
||||
expires = "; expires=" + date.toUTCString(); |
||||
} |
||||
document.cookie = name + "=" + value + expires + "; path=/"; |
||||
} |
||||
|
||||
function getCookie(name){ |
||||
var nameEQ = name + "="; |
||||
var ca = document.cookie.split(';'); |
||||
for (var i = 0; i < ca.length; i++){ |
||||
var c = ca[i]; |
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length); |
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
function deleteCookies(){ |
||||
for (var i = 0; i < arguments.length; i++) setCookie(arguments[i], "", -1); |
||||
} |
||||
|
||||
function deleteAllCookies(){ |
||||
var cookies = document.cookie.split(";"); |
||||
for (var i = 0; i < cookies.length; i++) deleteCookies(cookies[i].split("=")[0]); |
||||
} |
||||
|
||||
Array.prototype.shuffle = function(){ |
||||
let currentIndex = this.length, temporaryValue, randomIndex; |
||||
while (0 != currentIndex){ |
||||
randomIndex = floor(random() * currentIndex); |
||||
currentIndex -= 1; |
||||
temporaryValue = this[currentIndex]; |
||||
this[currentIndex] = this[randomIndex]; |
||||
this[randomIndex] = temporaryValue; |
||||
} |
||||
} |
||||
|
||||
Array.prototype.clone = function(){ |
||||
return this.slice(0); |
||||
}; |
||||
|
||||
Array.prototype.partitiate = function(dimensions){ |
||||
if (dimensions == 0) return this; |
||||
let parts = []; |
||||
while(this.length) parts.push(this.splice(0, round(pow(this.length, 1 / (1 + 1 / dimensions)))).partitiate(dimensions - 1)); |
||||
return parts; |
||||
} |
||||
|
||||
function debugInformation(){ |
||||
let x = viewPort.x, |
||||
y = viewPort.y; |
||||
textSize(12); |
||||
textStyle(NORMAL); |
||||
stroke(255); |
||||
strokeWeight(1); |
||||
fill(255); |
||||
text("FPS : " + round(frameRate()), 10 + x, 10 + textAscent("FPS : ") + y); |
||||
text("MouseX : " + round(mouseX + x), 10 + x, 10 + textAscent("FPS : ") + 10 + textAscent("MouseX : ") + y); |
||||
text("MouseY : " + round(-mouseY - y), 10 + x, 10 + textAscent("FPS : ") + 10 + textAscent("MouseX : ") + 10 + textAscent("MouseY : ") + y); |
||||
} |
||||
|
||||
|
||||
|
||||
function ColorPicker(editor) { |
||||
this.movingObject = ""; |
||||
|
||||
this.updateFromHSV = function(){ |
||||
this.h = 1 - $("#hue_picker").position().top / $("#hue").height(); |
||||
this.s = ($("#sb_picker").position().left + 8) / $("#saturation").width(); |
||||
this.v = 1 - ($("#sb_picker").position().top + 8) / $("#value").height(); |
||||
|
||||
this.r = HSVtoRGB(this.h, this.s, this.v).r; |
||||
this.g = HSVtoRGB(this.h, this.s, this.v).g; |
||||
this.b = HSVtoRGB(this.h, this.s, this.v).b; |
||||
|
||||
this.hex = RGBtoHEX(this.r, this.g, this.b); |
||||
|
||||
this.updateInterface(); |
||||
} |
||||
|
||||
this.updateFromRGB = function(){ |
||||
this.r = $($(".color_picker_rgb")[0]).val(); |
||||
this.g = $($(".color_picker_rgb")[1]).val(); |
||||
this.b = $($(".color_picker_rgb")[2]).val(); |
||||
|
||||
this.h = RGBtoHSV(this.r, this.g, this.b).h; |
||||
this.s = RGBtoHSV(this.r, this.g, this.b).s; |
||||
this.v = RGBtoHSV(this.r, this.g, this.b).v; |
||||
|
||||
this.hex = RGBtoHEX(this.r, this.g, this.b); |
||||
|
||||
this.updateFromHEX(null, true); |
||||
|
||||
this.updateInterface(); |
||||
} |
||||
|
||||
this.updateFromHEX = function(input, otf){ |
||||
if (!otf){ //Not on the fly
|
||||
if ($(input).val().isValidHEX()) this.hex = $(input).val(); |
||||
else { |
||||
alert("Error!"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
this.r = HEXtoRGB(this.hex).r; |
||||
this.g = HEXtoRGB(this.hex).g; |
||||
this.b = HEXtoRGB(this.hex).b; |
||||
|
||||
this.h = RGBtoHSV(this.r, this.g, this.b).h; |
||||
this.s = RGBtoHSV(this.r, this.g, this.b).s; |
||||
this.v = RGBtoHSV(this.r, this.g, this.b).v; |
||||
|
||||
this.updateInterface(); |
||||
} |
||||
|
||||
this.updateInterface = function(){ |
||||
var o = Math.round((this.r * 299 + (this.g * 587) + (this.b * 114)) / 1000); //https://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
|
||||
this.fontColor = (o > 125) ? "#000" : "#FFF"; |
||||
|
||||
$($(".color_picker_rgb")[0]).val(this.r); |
||||
$($(".color_picker_rgb")[1]).val(this.g); |
||||
$($(".color_picker_rgb")[2]).val(this.b); |
||||
|
||||
$("#color_picker_hex").val(this.hex); |
||||
|
||||
$("#hue_picker").css("top", (1 - this.h) * $("#hue").height()); |
||||
$("#sb_picker").css({ |
||||
"left": this.s * $("#saturation").width() - 8, |
||||
"top": (1 - this.v) * $("#value").height() - 8 |
||||
}); |
||||
|
||||
var sRGB = HSVtoRGB(this.h, 1, 1); |
||||
var saturationBackground = "linear-gradient(to right, #FFF 0%, rgb("
|
||||
+ sRGB.r + ","
|
||||
+ sRGB.g + ","
|
||||
+ sRGB.b + ") 100%)"; |
||||
|
||||
$("#saturation").css("background", saturationBackground); |
||||
|
||||
/*$($(".color_picker_rgb")[0]).css({ |
||||
"background-color": "rgb(" + this.r + ", 0, 0)" |
||||
}); |
||||
$($(".color_picker_rgb")[1]).css({ |
||||
"background-color": "rgb(0, " + this.g + ", 0)" |
||||
}); |
||||
$($(".color_picker_rgb")[2]).css({ |
||||
"background-color": "rgb(0, 0, " + this.b + ")" |
||||
});*/ |
||||
//Fancy but overloaded
|
||||
|
||||
$("#color_picker_hex").css({ |
||||
"background-color": this.hex, |
||||
"color": this.fontColor |
||||
}); |
||||
} |
||||
|
||||
this.mousePressed = function(){ |
||||
var x = winMouseX - $("#saturation").offset().left; |
||||
var y = winMouseY - $("#value").offset().top; |
||||
if (x > 0 && x < $("#saturation").width() && y > 0 && y < $("#value").height()){ |
||||
this.movingObject = "sb"; |
||||
} |
||||
if (x > $("#saturation").width() + 6 && x < $("#saturation").width() + 6 + $("#hue").width() && y > 0 && y < $("#hue").height()){ |
||||
this.movingObject = "hue"; |
||||
} |
||||
this.mouseDragged(); |
||||
} |
||||
|
||||
this.mouseDragged = function(){ |
||||
if (this.movingObject == "hue"){ |
||||
var objH = $("#hue"); |
||||
var picker = $("#hue_picker"); |
||||
var h = winMouseY - objH.offset().top; |
||||
if (h > 0 && h < objH.height()){ |
||||
picker.css("top", h - 1); |
||||
} else if (h > objH.height()){ |
||||
picker.css("top", objH.height() - 1); |
||||
} else if (h < 0){ |
||||
picker.css("top", -1); |
||||
} |
||||
} |
||||
|
||||
if (this.movingObject == "sb"){ |
||||
var objS = $("#saturation"); |
||||
var objV = $("#value"); |
||||
var picker = $("#sb_picker"); |
||||
var s = winMouseX - objS.offset().left; |
||||
var v = winMouseY - objV.offset().top; |
||||
if (s > 0 && s < objS.width()){ |
||||
picker.css("left", s - 8); |
||||
} else if (s < 0){ |
||||
picker.css("left", -8); |
||||
} else if (s < objS.width()){ |
||||
picker.css("left", objS.width() - 8); |
||||
} |
||||
if (v > 0 && v < objV.height()){ |
||||
picker.css("top", v - 8); |
||||
} else if (v < 0){ |
||||
picker.css("top", -8); |
||||
} else if (v > objV.height()){ |
||||
picker.css("top", objV.height() - 8); |
||||
} |
||||
} |
||||
|
||||
this.updateFromHSV(); |
||||
} |
||||
|
||||
this.mouseReleased = function(){ |
||||
this.movingObject = ""; |
||||
} |
||||
|
||||
this.getColor = function(){ |
||||
return this.hex; |
||||
} |
||||
} |
||||
|
||||
//www.stackoverflow.com -->
|
||||
function RGBtoHEX(r, g, b) { |
||||
var rgb = b | (g << 8) | (r << 16); |
||||
return '#' + (0x1000000 + rgb).toString(16).slice(1); |
||||
} |
||||
|
||||
function HEXtoRGB(hex) { |
||||
var shorthandRegex = /^#?([a-fA-F\d])([a-fA-F\d])([a-fA-F\d])$/i; |
||||
hex = hex.replace(shorthandRegex, function(m, r, g, b) { |
||||
return r + r + g + g + b + b; |
||||
}); |
||||
var result = /^#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/i.exec(hex); |
||||
return result ? { |
||||
r: parseInt(result[1], 16), |
||||
g: parseInt(result[2], 16), |
||||
b: parseInt(result[3], 16) |
||||
} : null; |
||||
} |
||||
|
||||
function HSVtoRGB(h, s, v) { |
||||
var r, g, b, i, f, p, q, t; |
||||
if (arguments.length === 1) { |
||||
s = h.s, v = h.v, h = h.h; |
||||
} |
||||
i = Math.floor(h * 6); |
||||
f = h * 6 - i; |
||||
p = v * (1 - s); |
||||
q = v * (1 - f * s); |
||||
t = v * (1 - (1 - f) * s); |
||||
switch (i % 6) { |
||||
case 0: r = v, g = t, b = p; break; |
||||
case 1: r = q, g = v, b = p; break; |
||||
case 2: r = p, g = v, b = t; break; |
||||
case 3: r = p, g = q, b = v; break; |
||||
case 4: r = t, g = p, b = v; break; |
||||
case 5: r = v, g = p, b = q; break; |
||||
} |
||||
return { |
||||
r: Math.round(r * 255), |
||||
g: Math.round(g * 255), |
||||
b: Math.round(b * 255) |
||||
}; |
||||
} |
||||
|
||||
function RGBtoHSV(r, g, b) { |
||||
if (arguments.length === 1) { |
||||
g = r.g, b = r.b, r = r.r; |
||||
} |
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b), |
||||
d = max - min, |
||||
h, |
||||
s = (max === 0 ? 0 : d / max), |
||||
v = max / 255; |
||||
|
||||
switch (max) { |
||||
case min: h = 0; break; |
||||
case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break; |
||||
case g: h = (b - r) + d * 2; h /= 6 * d; break; |
||||
case b: h = (r - g) + d * 4; h /= 6 * d; break; |
||||
} |
||||
|
||||
return { |
||||
h: h, |
||||
s: s, |
||||
v: v |
||||
}; |
||||
} |
||||
|
||||
String.prototype.isValidHEX = function(){ |
||||
return /(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(this); |
||||
} |
@ -0,0 +1,120 @@ |
||||
let debug = false; |
||||
let gravity = false; |
||||
let userIsWriting = false; |
||||
let grabbing = false; |
||||
let balls = []; |
||||
let attractions = []; |
||||
let objectSlot = null; |
||||
let graphicSlot = null; |
||||
let viewPort = {x: 0, y: 0, v: 0}; |
||||
let origin = {x: 0, y: 0}; |
||||
let colorPicker = null; |
||||
|
||||
function setup(){ |
||||
setFrameRate(60); |
||||
let c = createCanvas($("#canvasHolder").width(), $("#canvasHolder").height()); |
||||
c.parent("canvasHolder"); |
||||
graphicSlot = createGraphics($("#slot").width(), $("#slot").height()); |
||||
graphicSlot.parent("slot"); |
||||
graphicSlot.noStroke(); |
||||
$("#slot canvas").show(); |
||||
viewPort.x = -width / 2; |
||||
viewPort.y = -height / 2; |
||||
viewPort.v = 10; |
||||
colorPicker = new ColorPicker(); |
||||
colorPicker.hex = "#FF0000"; |
||||
colorPicker.updateFromHEX(null, true); |
||||
$("input").on("focus", function(){userIsWriting = true;}); |
||||
$("input").on("blur", function(){userIsWriting = false;}); |
||||
let canvas = $("#slot canvas"); |
||||
canvas.mousedown(function(){ |
||||
grabbing = true; |
||||
$("canvas, div").css({ |
||||
"cursor": "grabbing" |
||||
}); |
||||
$(window).mouseup(function(){ |
||||
if (!grabbing) return; |
||||
if (mouseX < width){ |
||||
if (objectSlot.isBall) balls.push(new Ball(mouseX + viewPort.x, mouseY + viewPort.y, objectSlot.r, objectSlot.c)); |
||||
if (objectSlot.isAttraction) attractions.push(new Attraction(mouseX + viewPort.x, mouseY + viewPort.y, objectSlot.r, objectSlot.c)); |
||||
canvas.off("mousedown", "**"); |
||||
} |
||||
grabbing = false; |
||||
$("#defaultCanvas0, div").css({ |
||||
"cursor": "auto" |
||||
}); |
||||
$("#slot canvas").css({ |
||||
"cursor": "grab" |
||||
}); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
function draw(){ |
||||
checkSlot(); |
||||
checkViewPort(); |
||||
background(50); |
||||
let grabCheck = false, |
||||
grabbingCheck = false; |
||||
for (let a of attractions){ |
||||
a.show(); |
||||
if (a.checkGrab()) grabCheck = true; |
||||
if (a.isGrabbed) grabbingCheck = true; |
||||
} |
||||
if (grabbingCheck){ |
||||
$("#defaultCanvas0").css({ |
||||
"cursor": "grabbing" |
||||
}); |
||||
} else if (grabCheck){ |
||||
$("#defaultCanvas0").css({ |
||||
"cursor": "grab" |
||||
}); |
||||
} else { |
||||
$("#defaultCanvas0").css({ |
||||
"cursor": "auto" |
||||
}); |
||||
} |
||||
for (let b of balls){ |
||||
b.attracted(); |
||||
b.move(); |
||||
b.show(); |
||||
if (debug) b.debugInformation(); |
||||
} |
||||
if (debug) debugInformation(); |
||||
} |
||||
|
||||
function checkViewPort(){ |
||||
if (keyIsDown(LEFT_ARROW) || keyIsDown(65) && !userIsWriting) viewPort.x -= viewPort.v; |
||||
if (keyIsDown(RIGHT_ARROW) || keyIsDown(68) && !userIsWriting) viewPort.x += viewPort.v; |
||||
if (keyIsDown(UP_ARROW) || keyIsDown(87) && !userIsWriting) viewPort.y -= viewPort.v; |
||||
if (keyIsDown(DOWN_ARROW) || keyIsDown(83) && !userIsWriting) viewPort.y += viewPort.v; |
||||
translate(-viewPort.x, -viewPort.y); |
||||
} |
||||
|
||||
function updateRadius(elem){ |
||||
let value = $(elem).val(); |
||||
$("#radiusRange").val(value); |
||||
$("#radiusInput").val(value); |
||||
} |
||||
|
||||
function checkSlot(){ |
||||
let val = $("#itemSelector").val(); |
||||
let radius = int($("#radiusInput").val()); |
||||
let color = colorPicker.getColor(); |
||||
switch(val){ |
||||
case "ball": |
||||
objectSlot = new Ball(graphicSlot.width / 2, graphicSlot.height / 2, radius, color); |
||||
objectSlot.isBall = true; |
||||
objectSlot.isAttraction = false; |
||||
break; |
||||
case "attractor": |
||||
objectSlot = new Attraction(graphicSlot.width / 2, graphicSlot.height / 2, radius, color); |
||||
objectSlot.isAttraction = true; |
||||
objectSlot.isBall = false; |
||||
break; |
||||
} |
||||
let o = objectSlot; |
||||
graphicSlot.clear(); |
||||
graphicSlot.fill(o.c); |
||||
graphicSlot.ellipse(o.pos.x, o.pos.y, o.r * 2); |
||||
} |
@ -0,0 +1,63 @@ |
||||
a:link, a:hover, a:active, a:visited{color: #000;} |
||||
|
||||
html, body{margin: 0; padding: 0; height: 100%; width: 100%;} |
||||
|
||||
canvas{margin: 0; padding: 0; border: none; display: block;} |
||||
|
||||
button:hover{cursor: pointer;} |
||||
|
||||
div{ |
||||
-webkit-user-select: none; |
||||
-moz-user-select: none; |
||||
-ms-user-select: none; |
||||
user-select: none; |
||||
} |
||||
|
||||
#canvasHolder{ |
||||
position: absolute; |
||||
width: calc(100% - 350px - 5px - 2 * 50px); |
||||
height: 100%; |
||||
} |
||||
|
||||
#mainInterface{ |
||||
position: absolute; |
||||
left: calc(100% - 350px - 5px - 2 * 50px); |
||||
width: 350px; |
||||
height: calc(100% - 2 * 50px); |
||||
padding: 50px; |
||||
border-left: 5px solid #000; |
||||
background-color: #AAA; |
||||
} |
||||
|
||||
#itemSelector{ |
||||
width: 200px; |
||||
height: 40px; |
||||
margin-left: 75px; |
||||
border-radius: 10px 0 0 10px; |
||||
font-size: 30px; |
||||
text-align: center; |
||||
} |
||||
|
||||
#addButton:hover{ |
||||
background-color: #1C1; |
||||
} |
||||
|
||||
#radiusRange{ |
||||
width: 100%; |
||||
margin: 70px 0 30px 0; |
||||
} |
||||
|
||||
span, #radiusInput{ |
||||
font-size: 20px; |
||||
width: 20%; |
||||
} |
||||
|
||||
#slot{ |
||||
width: 350px; |
||||
height: 20%; |
||||
border-radius: 20px; |
||||
background-color: #555; |
||||
} |
||||
#slot:hover{ |
||||
cursor: grab; |
||||
} |
After Width: | Height: | Size: 93 KiB |
Loading…
Reference in new issue