If you're seeing this message, it means we're having trouble loading external resources on our website.

তোমার যদি কোন ওয়েব ফিল্টার দেওয়া থাকে, তাহলে দয়া করে নিশ্চিত কর যে *.kastatic.org এবং *.kasandbox.org ডোমেইনগুলো উন্মুক্ত।

মূল বিষয়বস্তু

কণার ধরণ

এখন আমরা আরও উন্নত অবজেক্ট অরিয়েন্টেড প্রোগ্রামিং (object-oriented programming) পদ্ধতি ব্যবহার করবো। যেমন ইনহেরিটেন্স (inheritance)। সুতরাং জাভাস্ক্রিপ্ট পরিচিতি কোর্সে "ইনহেরিটেন্স" সম্পর্কিত ধারণাটি পর্যালোচনা করা উচিৎ। সময় নিয়ে অনুশীলনীটি দেখা উচিৎ!
ইনহেরিটেন্সের কার্যপ্রক্রিয়া জানতে পেরে কি ভালো লাগছে? ভালো, কারণ আমরা এই ইনহেরিটেন্স ব্যবহার করে বিভিন্ন ধরনের Particle সাব-অবজেক্ট তৈরি করব যেগুলোর কার্যকারিতা একই রকম হলেও অনেকগুলো দিক থেকে ভিন্নভাবে কাজ করে।
সাধারণ একটি Particle পর্যালোচনা করে দেখা যাক:
var Particle = function(position) {
  this.acceleration = new PVector(0, 0.05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = position.get();
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

Particle.prototype.display = function() {
  fill(127, 127, 127);
  ellipse(this.position.x, this.position.y, 12, 12);
};
Next, we create a new object type based on Particle, which we'll call Confetti. We'll start off with a constructor function that accepts the same number o arguments, and simply calls the Particle constructor, passing them along:
var Confetti = function(position) {
  Particle.call(this, position);
};
এখন আমাদের Confetti অবজেক্টটি যাতে Particle অবজেক্টের মত একই মেথড অনুসরণ করে তা আমাদের নিশ্চিত করতে হবে। বিশেষভাবে আমাদের উল্লেখ করে দিতে হবে যেন তাদের প্রোটোটাইপটি (prototype) Particle প্রোটোটাইপের উপরে ভিত্তি করে তৈরি হয়:
Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;
এই মুহূর্তে আমাদের কাছে Confetti অবজেক্ট আছে যেটি Particle অবজেক্টের মত একই রকম কাজ করে। ইনহেরিটেন্স মানে অনুলিপি তৈরি করা নয়, এটা একই বৈশিষ্ট্য সম্পন্ন নতুন অবজেক্ট তৈরি করে কিন্তু কিছু বৈশিষ্ট্য অন্যদের থেকে আলাদা হয়। তাহলে একটি Confetti অবজেক্ট কেমন ভাবে আলাদা হয়ে থাকে? নাম দেখেই মনে হচ্ছে যে, এটা দেখতে একটু ভিন্ন রকম হবে। আমাদের কাছে থাকা Particle অবজেক্টগুলো উপবৃত্তাকার হয়ে থাকে, কিন্তু confetti সাধারণত বর্গাকার কাগজের মত হয়ে থাকে। একদম শেষ পর্যায়ে তাই আমাদের display পদ্ধতিটি পরিবর্তন করতে হবে যাতে এগুলোকে আয়াতাকার করে দেখান সম্ভব হয়:
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255, this.timeToLive);
  stroke(0, 0, 0, this.timeToLive);
  strokeWeight(2);
  rect(0, 0, 12, 12);
};
এই যে এখানে একটি Particle অবজেক্টের উদাহরণসহ একটি প্রোগ্রাম এবং একটি Confetti অবজেক্টের উদাহরণসহ প্রোগ্রাম আছে। লক্ষ্য করলে দেখা যাচ্ছে যে, এগুলোর ব্যবহার এক রকম হলেও দেখতে কিন্তু আলাদা হয়ে থাকে:

ঘূর্ণন (rotation) সংযুক্তকরণ

এখন এটাকে একটু বাস্তবধর্মী করে তৈরি করা যাক। ধরি, Confetti কণাটি যখন বাতাসে ভেসে যাওয়ার সময় আমরা কণাটিকে ঘোরাতে (rotate) চাই। আমরা অবশ্যই কৌণিক বেগ (angular velocity) এবং ত্বরণের (acceleration) মডেল তৈরি করতে পারি, যেরকম আমরা স্পন্দনের (Oscillations) অনুশীলনীতে করেছিলাম। এর পরিবর্তে আমরা একটি দ্রুত সমাধান পদ্ধতি অনুসরণ করবো।
আমরা জানি যে একটি কণার একটি x অবস্থান থাকে, যেটা 0 এবং উইন্ডোর প্রস্থের মাঝে কোন একটি স্থানে থাকে। কি হবে এখন আমরা যদি বলি: যখন x কণার অবস্থান 0 তে তখন এর ঘূর্ণন হবে 0; আর যখন x কণার অবস্থান উইন্ডোর প্রস্থের সমান হবে, তখন এর ঘূর্ণন কি TWO_PI এর সমান হবে? শুনে কি কিছু মনে হচ্ছে? যখনই আমাদের কাছে একটি নির্দিষ্ট রেঞ্জের মান থাকবে আর আমরা ঐ মানটিকে অন্য একটি রেঞ্জে ম্যাপ করতে চাইব তখনই আমরা ProcessingJS এর map() ফাংশনটি ব্যবহার করে সহজভাবে এই নতুন মানটি হিসাব করতে পারব।
var theta = map(this.position.x, 0, width, 0, TWO_PI);
আর যদি আরও একটু ঘূর্ণন সংযুক্ত করতে চাই তাহলে কোণের রেঞ্জটি আমরা 0 থেকে TWO_PI*2 এর মধ্যে ম্যাপ করতে পারি। এখন দেখা যাক যে এই display() পদ্ধতির ভেতরে কোড কীভাবে কাজ করতে পারে।
Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255);
  stroke(0, 0, 0);
  strokeWeight(2);
  pushMatrix();
  translate(this.position.x, this.position.y);
  var theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
  rotate(theta);
  rect(0, 0, 12, 12);
  popMatrix();
};
এটা দেখতে অনেকটা এই রকম হবে - কার্যকর ঘূর্ণন দেখার জন্য প্রোগ্রামটি বেশ কয়েকবার রান করতে হবে:
থেটার (theta) ভিত্তিটি আমরা y অবস্থানের উপরে নির্ভর করে নির্ণয় করতে পারি। থেটা একটু অন্যরকম কাজ করে থাকে। এটা কেন হয়? কণার y দিক-নির্দেশক এ একটি শূন্য নয় এমন ধ্রুবক (non-zero constant) ত্বরণ আছে, যার অর্থ হল বেগ y সময়ের একটি সরল রৈখিক ফাংশন এবং y এর অবস্থানটি হল সময়ের একটি অধিবৃত্তিক ফাংশন। নিচের লেখচিত্রটি দেখলে এর দিয়ে কি বোঝানো হয়েছে তা দেখা যায় (উপরে প্রোগ্রামের উপরে ভিত্তি করে এটা তৈরি করা হয়েছে):
অর্থাৎ, আমরা যদি y অবস্থানের উপরে ভিত্তি করে confetti ঘূর্ণনটি নির্ণয় করি; ঘূর্ণনটি তাহলে পরাবৃত্তিক হবে। প্রকৃতভাবে বাতাসের ভিতর দিয়ে confetti এর নিচে পড়ার সময় যে ঘূর্ণনটি দেখা যায় সেটা বেশ জটিল হওয়ার কারণে সিমুলেশনটি যথাযথ হবে না। নিজে থেকেই আমরা চেষ্টা করে দেখতে পারি যে, এটা কতটুকু বাস্তবিক দেখা যায়! অন্য কোন ফাংশনের কথা কি চিন্তা করা যায় যেটা দেখতে আরও বাস্তবসম্মত হবে?"

একটি বিচিত্র কণা ব্যবস্থা

এখন অনেকগুলো Particle (কণা) এবং Confetti অবজেক্ট আমরা তৈরি করতে চাই। এই কারণেই আমরা ParticleSystem অবজেক্ট তৈরি করেছি, তাহলে আমরা কি Confetti অবজেক্টকে অনুসরণ করার জন্য এটার সাথে যোগ কর‍তে পারি? এটা করার একটি পদ্ধতি আছে, Particle অবজেক্টের কোড কপি করা:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = [];
};

ParticleSystem.prototype.addParticle = function() {
    this.particles.push(new Particle(this.origin));
    this.confettis.push(new Confetti(this.origin));
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
  }
for (var i = this.confettis.length-1; i >= 0; i--) {
    var p = this.confettis[i]; p.run();
  }
};
লক্ষ্য করলে দেখা যাবে যে আমাদের কাছে দুইটি আলাদা অ্যারে আছে, একটি অ্যারে কণার জন্য আর অন্যটি confetti এর জন্য। প্রতিবার কণা অ্যারেতে আমরা যদি কোন কিছু পরিবর্তন বা যোগ করি, তাহলে ঐ পরিবর্তনটুকু confetti অ্যারেতেও করতে হবে! এটা বিরক্তিকর একটি কাজ। এর অর্থ দাঁড়াচ্ছে আমাদেরকে দ্বিগুণ কোড লিখতে হচ্ছে, আর কোথাও যদি কোন পরিবর্তন করি তাহলে দুই জায়গাতে পরিবর্তনগুলো করতে হবে। এই দুই জায়গায় কাজ করাটা এড়িয়ে যাওয়া সম্ভব কারণ জাভাস্ক্রিপ্টে আমরা বিভিন্ন ধরনের অ্যারে সংরক্ষণ করে রাখতে পারি এবং আমাদের অবজেক্টের ইন্টারফেস একই রকমের থাকার কারণে - আমরা run() মেথড বলে কল করব। উভয় ধরনের অবজেক্ট-ই এই ইন্টারফেস দিয়ে সংজ্ঞায়িত করা সম্ভব। যেখানে আমাদের একটি অ্যারে সংরক্ষণ করা আছে সেখানে ফিরে যাব এবং সিদ্ধান্ত নিব যে কোন ধরনের কণা যোগ করব এবং ঐ অ্যারের ভেতর দিয়ে প্রক্রিয়াটি আবার পরিচালনা করবো। এটা খুবই সাধারণ একটি পরিবর্তন - আমাদের যেটা করতে হবে তা হল addParticle মেথড একটু পরিবর্তন করে ব্যবহার করা:
var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  var r = random(1);
  if (r < 0.5) {
    this.particles.push(new Particle(this.origin));
  } else {
    this.particles.push(new Confetti(this.origin));
  }
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
    if (p.isDead()) {
      this.particles.splice(i, 1);
    }
  }
};
এখনে সবকিছু একসাথে দেওয়া হল!