Som tidigare nämnt skulle studiens resultat kunna styrkas ytterligare om en helt automatiserad utvärderingsprocess utvecklas. Detta skulle innebära att eventuell påverkan av användarinput på mätresultatet skulle försvinna samt att det skulle göra det möjligt att genomföra många fler testomgångar för att på så sätt öka säkerheten i mätresultatet. Det skulle också vara av intresse att genomföra någon form av användarstudie för att på så vis validera de resultat som presenterats i studien eftersom uppmätta skillnader i prestanda inte behöver reflektera skillnader i den prestanda en användare upplever (Mallik m.fl., 2008).
Som framkommer av mätresultaten är skillnaderna i uppmätt prestanda relativt lika mellan de testkonfigurationer som använder två respektive fyra aktiva processorkärnor. Detta förhållande skulle behöva undersökas närmare. Denna studie har endast genomfört mätningar med dessa två hårdvarukonfigurationer. Det skulle vara av intresse att vidga studien genom att utföra mätningar med fler typer av hårdvarukonfigurationer för att närmare kontrollera hur valet av parallelliseringsstrategi påverkas av vilken hårdvara som används. Ett exempel på en sådan utveckling av detta arbete skulle kunna vara att genomföra studier med mobila hårdvaruplattformar och de mjukvaror som används av dessa. Detta eftersom det blir vanligare med parallell hårdvara i mobila enheter samt att dessa oftast har mer begränsade hårdvaruresurser än vad motsvarande desktopenheter vilket gör att det möjligen är av ännu större vikt att utnyttja dessa resurser så effektivt som möjligt (Asanovic m.fl., 2009).
Studiens resultat visar på ett samband mellan antalet skickade meddelanden i
kommunikationen till och från Web workers men detta förhållande behöver undersökas
närmare. Enligt Erbad m.fl. (2011) så tar det längre tid att skicka flera små meddelanden än
ett större som är lika stort som alla de mindre meddelandena tillsammans. Vad som skulle
behöva undersökas är mer exakt är hur detta förhållande ser ut och då kanske i första hand
för de parallelliseringsstrategier som implementerats i den här studien. Men eftersom denna
studie inte omfattar samtliga möjliga parallelliseringsstrategier finns det också ett intresse i
att identifiera och utvärdera vilka andra parallelliseringsstrategier som skulle kunna vara
tillämpliga för Web workers samt hur dessa påverkas av de begränsningar som finns för Web
workers. Ett alternativ till vidare utveckling av studien skulle kunna vara att utveckla Web
worker API:et så att Web workers kan dela minne med andra exekveringstrådar. Detta skulle
kunna vara både mellan huvudtråden och mellan olika Web worker trådar. En sådan
utveckling av Web worker-teknologin är intressant eftersom den eventuellt skulle kunna
avhjälpa några av de problem som nuvarande implementation av Web worker API:et för med
sig.
39
En vidareutveckling av studien skulle kunna introducera andra typer av objekt i fysiksimuleringen utöver rep-objekt för att undersöka hur detta påverkar de olika parallelliseringsstrategierna. En senare utvidgning av studien skulle också kunna omfatta parallellisering av mer avancerade fysiksimuleringssystem för att se vilken skillnad detta skulle göra för parallelliseringsprocessen. Detta skulle senare kunna vidareutvecklas genom att applicera studiens resultat på andra typer av applikationer som använder partikelsystem.
Därefter skulle det också vara av intresse att undersöka huruvida resultaten går att överföra på andra typer av applikationer som inte baseras på just partikelsystem. I detta arbete har endast en del av applikationen parallelliserats. Vad som skulle behöva undersökas vidare är vilka möjligheter som finns att parallellisera fler delar av en applikation och vilka delar som överhuvudtaget går att parallellisera med hjälp av Web workers. En intressant infallsvinkel är att undersöka om det är möjligt att parallellisera hela applikationer genom att placera ut samtliga delar i Web workers.
Något som också skulle kunna undersökas närmare är om användandet av ett annat API för utritning som t.ex. WebGL skulle kunna innebära förändrade förutsättningar för parallelliseringsmöjligheterna och vilka prestanda effekter detta skulle ha för olika parallelliseringsstrategier. WebGL är en standard under utveckling för att rendera grafikkortsaccelererad grafik i webbläsare (Anttonen m.fl., 2011). Det skulle därför vara intressant att utvärdera parallelliseringsmöjligheterna tillsammans med Web workers för att på så vis bidra till utvecklingen av båda dessa API:er och samverkan dem emellan. En annan infallsvikel på fortsatt arbete skulle kunna vara en vidareutveckling av canvas-elementet så att detta kan göras mer parallelliserbart, t.ex. genom att implementera en utritningsbuffert och ge Web workers tillgång till denna.
Som visats av Okamoto och Kohana (2010) är det möjligt att vidareutveckla Web workers så
att de kan distribueras mellan en klient och en server. Ytterligare ett spår att arbeta vidare på
är att undersöka möjligheterna till att utveckla Web workers så att de kan användas till
distribuerade beräkningar där en server kan överlåta beräkningar på klienter. En nytta med
detta skulle kunna vara att på så vis utnyttja oanvänd beräkningskraft vilket innebär ett
effektivare användande av tillgängliga resurser. Detta skulle avlasta servern så att denna kan
ta hand om fler inkommande anrop. Till exempel skulle det kunna fungera på så vis att om en
server får in ett tyngre beräkningsarbete kan denna skicka en förfrågan till anslutna klienter
med lediga resurser och överlåta hela eller delar av beräkningsarbetet till de klienter som har
för tillfället inte använder sin fulla beräkningskraft. På så vis skulle samtliga ansluta klienter
kunna dra nytta av de beräkningsresurser som tillhandahålls av alla klienterna tillsammans.
40
Referenser
Agarwal, M. och Saha, S. (2011) Learning chemistry through puzzle based game: Atoms to
Molecule, 2011 9th International Conference on Emerging eLearning Technologies andApplications (ICETA), Oktober 2011, s. 189 –193.
Ahmad, A., Adly, S., Terraz, O. och Ghazanfarpour, D. (2007) Stability Analysis of Filtered
Mass-Spring Systems, Proceedings of the 2007 Theory and Practice of ComputerGraphic, Bangor, United Kingdom, Eurographics Association. s. 45–52.
Anttonen, M., Salminen, A., Mikkonen, T. och Taivalsaari, A. (2011) Transforming the web
into a real application platform: new technologies, emerging trends and missing pieces, Proceedings of the 2011 ACM Symposium on Applied Computing SAC ’11, NewYork, NY, USA, ACM, s. 800–807.
Asanovic, K., Bodik, R., Demmel, J., Keaveny, T., Keutzer, K., Kubiatowicz , J., Morgan, N., Patterson, D., Sen, K., Wawrzynek, J., Wessel, D. och Yelick, K. (2009) ‘A view of the parallel computing landscape’, Communications of the ACM, Vol 52 (10), s. 56–67.
Bouvier, E., Cohen, E. och Najman, L. (1997) ‘From crowd simulation to airbag deployment:
particle systems, a new paradigm of simulation’, Journal of Electronic Imaging, Vol 6, s.
94–107.
Claypool, M. och Claypool, K. (2009) Perspectives, frame rates and resolutions: it’s all in the
game, Proceedings of the 4th International Conference on Foundations of DigitalGames, FDG ’09, New York, NY, USA, ACM, s. 42–49.
Erbad, A.M., Hutchinson, N.C. och Krasic, C. (2011) Scalable quality for web-based games, Proceedings of the 1st ACM SIGPLAN international workshop on Programming
language and systems technologies for internet clients, PLASTIC ’11, New York, NY, USA, ACM, s. 57–60.
Fortuna, E., Anderson, O., Ceze, L. och Eggers, S. (2010) A limit study of JavaScript
parallelism, 2010 IEEE International Symposium on Workload Characterization (IISWC), s. 1–10.Hughes, C.J., Grzeszczuk, R., Sifakis, E., Kim, D., Kumar, S., Selle, A.P., Chhugani, J., Holliman, M. och Chen, Y.K. (2007) ‘Physical simulation for animation and visual effects: parallelization and characterization for chip multiprocessors’, SIGARCH
Comput. Archit. News, Vol 35 (2), s. 220–231.Jakobsen, T. (2001) Advanced character physics, Proceedings of the Game Developers Conference 2001, San Jose, CMP Media.
Jiang, Y. och Liu, Z. (2008) Application and Research of Numerical Integration in Cloth
Simulation, International Symposium on Computer Science and ComputationalTechnology 2008, ISCSCT ’08, December, s. 128 –132.
Jiang, Y. och Wang, R. (2010) Real-time cloth simulation based on improved Verlet
algorithm, 2010 IEEE 11th International Conference on Computer-Aided IndustrialDesign Conceptual Design (CAIDCD), November, s. 443 –446.
41
Kačić-Alesić, Z., Nordenstam, M. och Bullock, D. (2003) A practical dynamics system, Proceedings of the 2003 ACM SIGGRAPH/Eurographics symposium on Computer animation, SCA ’03, Aire-la-Ville, Switzerland, Eurographics Association, s. 7–16.
Kim, H., Raman, A., Liu, F., Lee, J.W. och August, D.I. (2010) Scalable Speculative
Parallelization on Commodity Clusters, 2010 43rd Annual IEEE/ACM InternationalSymposium on Microarchitecture (MICRO), December, s. 3 –14.
Mallik, A., Cosgrove, J., Dick, R.P., Memik, G. och Dinda, P. (2008) PICSEL: measuring
user-perceived performance to control dynamic frequency scaling, Proceedings of the13th international conference on Architectural support for programming languages and operating systems, ASPLOS XIII, New York, NY, USA, ACM. s. 70–79.
Martinsen, J.K. och Grahn, H. (2011) Thread-level speculation as an optimization technique
in Web Applications - Initial results, 2011 6th IEEE International Symposium onIndustrial Embedded Systems (SIES), Juni, s. 83 –86.
Mehrara, M., Hsu, P.-C., Samadi, M. och Mahlke, S. (2011) Dynamic parallelization of
JavaScript applications using an ultra-lightweight speculation mechanism, 2011 IEEE17th International Symposium on High Performance Computer Architecture (HPCA), Februari, s. 87 –98.
Muoz, K., Noguez, J., McKevitt, P., Neri, L., Robledo-Rella, V. och Lunney, T. (2009) Adding
features of educational games for Teaching Physics, 2009 39th IEEE Frontiers inEducation Conference, FIE ’09, Oktober, s. 1 –6.
Müller, M., Heidelberger, B., Hennix, M. och Ratcliff, J. (2007) ‘Position based dynamics’,
Journal of Visual Communication and Image Representation, Vol 18(2), s. 109 - 118.Okamoto, S. och Kohana, M. (2010) Load distribution by using web workers for a real-time
web application, Proceedings of the 12th International Conference on InformationIntegration and Web-based Applications & Services iiWAS’10, New York, NY, USA, ACM, s. 592–597.
Ratanaworabhan, P., Livshits, B. och Zorn, B.G. (2010) JSMeter: comparing the behavior of
JavaScript benchmarks with real web applications, Proceedings of the 2010 USENIXconference on Web application development, WebApps’10, Berkeley, CA, USA, USENIX Association, s. 27–38.
Reeves, W.T. (1983) ‘Particle systems - a technique for modeling a class of fuzzy objects’,
SIGGRAPH Computer Graphics, Vol 17(3), s. 359–375.Reynolds, C.W. (1987) Flocks, Herds, and Schools: A Distributed Behavioral Model, SIGGRAPH '87 Proceedings of the 14th annual conference on Computer graphics and interactive techniques, New York, NY, USA, ACM, s. 25–34.
Richards, G., Lebresne, S., Burg, B. och Vitek, J. (2010) An analysis of the dynamic behavior
of JavaScript programs, Proceedings of the 2010 ACM SIGPLAN conference onProgramming language design and implementation, PLDI ’10, New York, NY, USA, ACM, s. 1–12.
Sims, K. (1990) ‘Particle animation and rendering using data parallel computation’,
SIGGRAPH Computer Graphics, Vol 24(4), s. 405–413.42
Taivalsaari, A. och Mikkonen, T. (2011) The Web as an Application Platform: The Saga
Continues, 2011 37th EUROMICRO Conference on Software Engineering and AdvancedApplications (SEAA), 30 September, s. 170 –174.
Verlet, L. (1967) ‘Computer “Experiments” on Classical Fluids. I. Thermodynamical Properties of Lennard-Jones Molecules’, Physical Review. Vol 159(1), s. 98–103.
WHATWG (2012a) HTML Living Standard, <http://www.whatwg.org/specs/web-apps/current-work/> (7 februari 2012).
WHATWG (2012b) Web workers – HTML standard, <http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#workers> (2 februari 2012).
W3C (2011a) HTML5 a vocabulary and associated APIs for HTML and XHTML W3C
working draft 25 May 201, <http://www.w3.org/TR/2011/WD-html5-20110525/>(7 februari 2012).
W3C (2011b) Web workers W3C working draft 01 September 2011.
<http://www.w3.org/TR/2011/WD-workers-20110901/> (2 februari 2012).
Yeh, T.Y., Faloutsos, P., Patel, S.J. och Reinman, G. (2007) ParallAX: an architecture for
real-time physics, Proceedings of the 34th annual international symposium onComputer architecture, ISCA ’07, New York, NY, USA, ACM, s. 232–243.
Yeh, T.Y., Reinman, G., Patel, S.J. och Faloutsos, P. (2009) ‘Fool me twice: Exploring and exploiting error tolerance in physics-based animation’, ACM Transactions on Graphics, Vol 29(1), s. 5:1–5:11.
ZeptoLab (2010) Cut the Rope, Datorspel, ZeptoLab.
2D Boy (2008) World of Goo, Datorspel, 2D Boy.
43
Appendix A - Programkod för den
icke-parallelliserade versionen av applikationen
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>p</title>
<script type="text/javascript" src="particle.js"
charset="utf-8"></script>
<script type="text/javascript" src="rope.js" charset="utf-8"></script>
<script type="text/javascript" src="control.js" charset="utf-8"></script>
<script type="text/javascript"
src="https://www.google.com/jsapi"></script>
<meta name="viewport" content="width=device-width, initial-scale=0.5, user-scalable=no">
<style>
body { -webkit-user-select: none; }
#wrapper{width:800px;margin:0 auto;}
#results{ -webkit-user-select: text; }
#debug{ -webkit-user-select: text; } canvas{
border: 1px solid #000;
background-color: #dac8a0;
/*cursor:pointer;cursor:hand;*/
-webkit-user-select: none;
-webkit-touch-callout:none;
-webkit-tap-highlight-color:transparent;
}
input,label,button{display:none;}
</style>
</head>
<body>
<p id="version"></p>
<div id="wrapper">
<div>
<h2>Without Web workers</h2>
<label>Mouse action:</label>
<input type="radio" name="mouseAction" id="dragRadio"
value="drag" onclick="updateMouseAction();"/>
<label for="dragRadio">Drag</label>
44
<input type="radio" name="mouseAction"
id="disconnectRadio" value="disc" checked="checked"
onclick="updateMouseAction();"/>
<label for="disconnectRadio">Disconnect</label>
<br />
<label>Options:</label>
<input type="checkbox" name="drawDots" id="drawDots" />
<label for="drawDots">Draw dots</label>
<input type="checkbox" name="extraDrag" id="extraDrag"
/>
<label for="extraDrag">Extra drag</label>
<input type="checkbox" name="timeCorrectedVerlet"
id="timeCorrectedVerlet" />
<label
for="timeCorrectedVerlet">timeCorrectedVerlet</label>
<button onclick="togglePause();">pause</button>
</div>
<p id="fps">fps</p>
<canvas id="canvas"></canvas>
</div>
<!--<button onclick="updateParticles();">update</button>-->
<div id="chart_div"></div>
<div id="results" style="display:none">
<table border="1">
<tr><th>Game lasted</th><th>Avg. FPS</th><th>Highest FPS</th><th>Lowest FPS</th><th>Median FPS</th><th>Fastest phys.sim.
updt</th><th>Slowest phys.sim. updt</th><th>Avg. phys.sim.
updt</th><th>Median phys.sim. updt</th></tr>
<tr><td id="gameLasted"></td><td id="avgFPS"></td><td id="highestFPS"></td><td id="lowestFPS"></td><td id="medianFPS">...calculation in progress</td><td id="fastestPhys"></td><td id="slowestPhys"></td><td id="avgPhys"></td><td id="medianPhys">...calculation in progress</td></tr>
</table>
</div>
<div id="debug"><div>
</body>
</html>
control.js
var fps = 0, now, lastUpdate = (new Date)*1 - 1;
var fpsFilter = 50;
var fpsCalcInterval=setInterval(function(){
//fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
var fpsArray=new Array();
var speedArray=new Array();
var gameStartTime=new Date().getTime();
var updateStartTime;
var canvas;
var pause=false;
45
var mouseDown=false;var cuttingMovePositions=new Array();
var draggedParticle=null;
var drawDots;
var extraDrag;
var fatParticle;
var theGoal;
var gameObjects=new Array();
var gameReset=false;
var fadeAlpha=1.0;
var level=0;
var constraintsIterations=6;
var ballImage=new Image();
var bigKnobImage=new Image();
var smallKnobImage=new Image();
window.onload=function(){
gameStartTime=new Date().getTime();
lastUpdateTime=new Date().getTime();
drawDots=document.getElementById("drawDots");
extraDrag=document.getElementById("extraDrag");
canvas=document.getElementById("canvas");
canvas.width=800;
canvas.height=600;
//Set mouse events
canvas.onmouseout=mouseReset;
updateMouseAction();
document.body.addEventListener('touchmove', function(event) { event.preventDefault();
}, false);
canvas.addEventListener("touchstart",startTouchCutDrag,false);
canvas.addEventListener("touchmove",doTouchCutDrag,false);
canvas.addEventListener("touchend",endCutDrag,false);
level=0;
ballImage.src="images/ball.png";
ballImage.onload=function(){
bigKnobImage.src="images/big_knob.png";
bigKnobImage.onload=function(){
smallKnobImage.src="images/small_knob.png";
smallKnobImage.onload=init;
46
}};
}
function init(){
var gameOver=false;
gameObjects=new Array();
gameReset=false;
if(level==0){
fatParticle=new particle(374,200,374,212,0,0,5,false);
fatParticle.radius=11.75;
gameObjects.push(fatParticle);
var rope1=new rope(177,197,28); //x y length gameObjects.push(rope1);
var rope2=new rope(379,31,27);
gameObjects.push(rope2);
var rope3=new rope(379,387,26);
gameObjects.push(rope3);
var rope4=new rope(590,197,28);
gameObjects.push(rope4);
theGoal=new goal(507,490);
} else if(level==1){
fatParticle=new particle(181,214,181,214,0,0,5,false);
fatParticle.radius=11.75;
gameObjects.push(fatParticle);
var rope1=new rope(50,50,30); //x y length gameObjects.push(rope1);
var rope2=new rope(250,50,25);
gameObjects.push(rope2);
var rope3=new rope(250,250,40);
gameObjects.push(rope3);
var proxRope1=new proximityRope(428,332, 65);
gameObjects.push(proxRope1);
var proxRope2=new proximityRope(643,420, 70);
gameObjects.push(proxRope2);
theGoal=new goal(743,520);
} else {
47
displayResults();gameOver=true;
}
if(!gameOver){
//fps calc
//fps = 0, now, lastUpdate = (new Date)*1 - 1;
fpsFilter = 50;
var fpsOut = document.getElementById('fps');
clearInterval(fpsCalcInterval);
fpsCalcInterval=setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
fadeAlpha=1.0;
//Start drawing drawAll();
//Start verlet
updateStartTime=new Date().getTime();
updateAll(true);
} }
function medianWorkerMsgRec(e){
if(e.data.msg=="medianCalculated"){
document.getElementById("medianPhys").innerHTML=e.data.median;
if(c=canvas.getContext("2d")){
c.clearRect(0,390,canvas.width, 440);
c.fillStyle="#000000";
c.font = "20pt Arial";
c.textAlign= "center";
c.fillText("Median phys. sim. speed:
"+e.data.median+"ms/update", canvas.width/2+0.5, 410+0.5);
} } }
function medianFPSWorkerMsgRec(e){
if(e.data.msg=="medianCalculated"){
document.getElementById("medianFPS").innerHTML=parseFloatToUseComm aSep(e.data.median);
} }
function parseFloatToUseCommaSep(number){
var num=number.toFixed(3);
return num.replace(".", ",");
}
function displayResults(){
48
gameLastedTime=new Date().getTime()-gameStartTime;
var medianFPSWorker=new Worker("medianWorker.js");
medianFPSWorker.onmessage=medianFPSWorkerMsgRec;
medianFPSWorker.onerror=function(e){
alert(e.message+"\n in: "+e.filename+"\n at line: "+e.lineno);
e.preventDefault();
};
medianFPSWorker.postMessage({list:fpsArray});
var highestFps=undefined; var lowestFps=undefined;
var fpsSum=0;
for(var i=fpsFilter;i<fpsArray.length;i++){
if(highestFps==undefined || fpsArray[i]>highestFps) highestFps=fpsArray[i];
if(lowestFps==undefined || fpsArray[i]<lowestFps) lowestFps=fpsArray[i];
fpsSum+=fpsArray[i];
}
var avgFps=fpsSum/fpsArray.length;
var fastestUpdate=undefined; var slowestUpdate=undefined;
var updateTimeSum=0;
for(var i=0;i<speedArray.length;i++){
if(slowestUpdate==undefined || speedArray[i]>slowestUpdate) slowestUpdate=speedArray[i];
if(fastestUpdate==undefined || speedArray[i]<fastestUpdate) fastestUpdate=speedArray[i];
updateTimeSum+=speedArray[i];
}
var medianWorker=new Worker("medianWorker.js");
medianWorker.onmessage=medianWorkerMsgRec;
medianWorker.onerror=function(e){
alert(e.message+"\n in: "+e.filename+"\n at line: "+e.lineno);
e.preventDefault();
};
var avgSpeed=updateTimeSum/speedArray.length;
medianWorker.postMessage({list:speedArray});
document.getElementById("results").style.display="block";
document.getElementById("gameLasted").innerHTML=parseFloatToUseCom maSep(gameLastedTime/1000);
document.getElementById("avgFPS").innerHTML=parseFloatToUseCommaSe p(avgFps);
document.getElementById("highestFPS").innerHTML=parseFloatToUseCom maSep(highestFps);
49
document.getElementById("debug").innerHTML+="<br />All phys.sim.
update times:";
var table = document.createElement("table");
for(var i=0;i<speedArray.length;i++){
var tableRow=document.createElement("tr");
var tableData2=document.createElement("td");
tableData2.innerHTML=speedArray[i];
c.clearRect(0,0,canvas.width, canvas.height);
c.fillStyle="#000000";
c.font = "20pt Arial";
c.textAlign= "center";
c.fillText("Game lasted: "+(gameLastedTime/1000)+"s", canvas.width/2+0.5, 200+0.5);
c.fillText("Avg. FPS: "+roundNrToDecimal(avgFps,3)+"fps", canvas.width/2+0.5, 230+0.5);
c.fillText("Highest FPS:
"+roundNrToDecimal(highestFps,3)+"fps", canvas.width/2+0.5, 260+0.5);
c.fillText("Lowest FPS:
"+roundNrToDecimal(lowestFps,3)+"fps", canvas.width/2+0.5, 290+0.5);
c.fillText("Fastest physicssimulation update:
"+roundNrToDecimal(fastestUpdate,3)+"ms", canvas.width/2+0.5, 320+0.5);
c.fillText("Slowest physicssimulation update:
"+roundNrToDecimal(slowestUpdate,3)+"ms", canvas.width/2+0.5, 350+0.5);
c.fillText("Avg phys. sim. speed:
"+roundNrToDecimal(avgSpeed,3)+"ms/update", canvas.width/2+0.5, 380+0.5);
c.fillText("Median phys. sim. speed: ...calculation in progress", canvas.width/2+0.5, 410+0.5);
}
}
function togglePause(){
if(pause)
pause=false;
else
pause=true;
}
50
function updateMouseAction(){var dragRadio=document.getElementById("dragRadio");
if(dragRadio.checked){
canvas.removeEventListener("mousedown",startMouseCutDrag,false);
canvas.removeEventListener("mousemove",doMouseCutDrag,false);
canvas.removeEventListener("mouseup",endCutDrag,false);
canvas.addEventListener("mousedown",startDrag,false);
canvas.addEventListener("mousemove",doDrag,false);
canvas.addEventListener("mouseup",endDrag,false);
} else {
canvas.removeEventListener("mousedown",startDrag,false);
canvas.removeEventListener("mousemove",doDrag,false);
canvas.removeEventListener("mouseup",endDrag,false);
canvas.addEventListener("mousedown",startMouseCutDrag,false);
canvas.addEventListener("mousemove",doMouseCutDrag,false);
canvas.addEventListener("mouseup",endCutDrag,false);
} }
function mouseReset(e){
if(mouseDown){
mouseDown=false;
canvas.style.cursor = "default";
if(draggedParticle!=null){
draggedParticle.isDragged=false;
} } }
function startMouseCutDrag(e){
var x=e.pageX;
var y=e.pageY;
startCutDrag(x,y, e.target);
}
function startTouchCutDrag(e){
if (e.targetTouches.length>0) { var touch = e.targetTouches[0];
x=touch.pageX;
y=touch.pageY;
}
startCutDrag(x,y,e.target);
}
function startCutDrag(x,y,target){
mouseDown=true;
var pos_x_y=findPosition(target);
x -= pos_x_y[0];
y -= pos_x_y[1];
cuttingMovePositions=new Array();
51
cuttingMovePositions.push({x:x,y:y});
setTimeout(animateCutMove, 10);
canvas.style.cursor = "pointer";
}
function animateCutMove(){
if(cuttingMovePositions.length>0){
if(cuttingMovePositions.length>25) cuttingMovePositions.splice(0,5);
else
cuttingMovePositions.splice(0,1);
setTimeout(animateCutMove, 10);
} else if(mouseDown){
setTimeout(animateCutMove, 10);
} }
function doMouseCutDrag(e){
if(mouseDown){
var x=e.pageX;
var y=e.pageY;
doCutDrag(x,y,e.target);
} }
function doTouchCutDrag(e){
if(mouseDown){
if (e.targetTouches.length>0) { var touch = e.targetTouches[0];
x=touch.pageX;
y=touch.pageY;
}
doCutDrag(x,y,e.target);
} }
function doCutDrag(x,y,target){
if(mouseDown){
var pos_x_y=findPosition(target);
x -= pos_x_y[0];
y -= pos_x_y[1];
cuttingMovePositions.push({x:x,y:y});
var hitParticle=null;
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor == rope){
for(var j=0;j<gameObjects[i].particles.length;j++){
if(gameObjects[i].particles[j].connectedTo.length>0){
for(var q=0;q<cuttingMovePositions.length-1;q++){
52
//lineLineCollCheck({x1:??, y1:??, x2:??, y2:??},{x1:??, y1:??, x2:??, y2:??})
if(lineLineCollCheck({x1:cuttingMovePositions[q].x,
y1:cuttingMovePositions[q].y, x2:cuttingMovePositions[q+1].x, y2:cuttingMovePositions[q+1].y},
{x1:gameObjects[i].particles[j].x, y1:gameObjects[i].particles[j].y,
x2:gameObjects[i].particles[j].connectedTo[0].x, y2:gameObjects[i].particles[j].connectedTo[0].y})
){
hitParticle=gameObjects[i].particles[j].connectedTo[0];
}
} }
} } }
if(hitParticle!=null){
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor == particle){
releaseConnection(hitParticle, gameObjects[i])
} else if(gameObjects[i].constructor == rope){
for(var
j=0;j<gameObjects[i].particles.length;j++){
releaseConnection(hitParticle, gameObjects[i].particles[j]);
if(gameObjects[i].particles[j]==hitParticle){
var ropeIndex=i;
gameObjects[ropeIndex].isCut=true;
removeCutRopes();
} }
} } } } }
function endCutDrag(e){
mouseDown=false;
canvas.style.cursor = "default";
}
function startDrag(e){
53
var x=e.pageX;var y=e.pageY;
var pos_x_y=findPosition(e.target);
x -= pos_x_y[0];
y -= pos_x_y[1];
hitParticle=findParticle(x,y);
document.getElementById("debug").innerHTML="KLICK"+x+" "+y;
if(hitParticle!=null){
mouseDown=true;
draggedParticle=hitParticle;
draggedParticle.isDragged=true;
} }
function doDrag(e){
if(mouseDown){
var x=e.pageX;
var y=e.pageY;
var pos_x_y=findPosition(e.target);
x -= pos_x_y[0];
y -= pos_x_y[1];
draggedParticle.x=x;
draggedParticle.y=y;
} }
function endDrag(e){
mouseDown=false;
if(draggedParticle!=null){
draggedParticle.isDragged=false;
} }
function findParticle(x,y){
var particleHit=null;
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor == particle){
var deltaX=x-gameObjects[i].x;
var deltaY=y-gameObjects[i].y;
var dist=Math.sqrt(deltaX*deltaX+deltaY*deltaY);
if(dist<gameObjects[i].radius*2){
particleHit=gameObjects[i];
}
} else if(gameObjects[i].constructor == rope){
for(var j=0;j<gameObjects[i].particles.length;j++){
var deltaX=x-gameObjects[i].particles[j].x;
var deltaY=y-gameObjects[i].particles[j].y;
var dist=Math.sqrt(deltaX*deltaX+deltaY*deltaY);
if(dist<gameObjects[i].particles[j].radius*2){
particleHit=gameObjects[i].particles[j];
}
54
}} else if(gameObjects[i].constructor == proximityRope){
var deltaX=x-gameObjects[i].x;
var deltaY=y-gameObjects[i].y;
var dist=Math.sqrt(deltaX*deltaX+deltaY*deltaY);
if(dist<gameObjects[i].radius*2){
particleHit=gameObjects[i];
}
} else if(gameObjects[i].constructor == goal){
var deltaX=x-gameObjects[i].x;
var deltaY=y-gameObjects[i].y;
var dist=Math.sqrt(deltaX*deltaX+deltaY*deltaY);
if(dist<gameObjects[i].radius*2){
particleHit=gameObjects[i];
} } }
return particleHit;
}
function releaseConnection(p, p2){ //Remove all connections to p from p2
for(var i=0;i<p2.connectedTo.length;i++){
if(p2.connectedTo[i] == p){
p2.connectedTo.splice(i,1);
} } }
function updateAll(runCont){
if(!pause){
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor==particle){
gameObjects[i].accumulateForces();
} else if (gameObjects[i].constructor==rope){
for(var j=0;j<gameObjects[i].particles.length;j++){
gameObjects[i].particles[j].accumulateForces();
} } }
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor==particle){
gameObjects[i].verlet();
} else if (gameObjects[i].constructor==rope){
for(var j=0;j<gameObjects[i].particles.length;j++){
gameObjects[i].particles[j].verlet();
} } }
55
for(var loop=0;loop<constraintsIterations;loop++){
for(var i=0;i<gameObjects.length;i++){
if(gameObjects[i].constructor==particle){
gameObjects[i].satisfyConstraints();
} else if (gameObjects[i].constructor==rope){
} else if (gameObjects[i].constructor==rope){