এই লেখার সারাংশ

  • ChatGPT-এর সাথে জুটি বেঁধে ব্রাউজার-ভিত্তিক SVG এডিটর তৈরি করতে গিয়ে যে সব হোঁচট খেয়েছি তার সংকলন। টুলের জাপানি সংস্করণ বর্তমানে /tools/svgedit/ ঠিকানায় পাওয়া যায়।
  • একা এই প্রকল্প শেষ করা সম্ভব ছিল না। জেনারেটিভ এআইয়ের কোডিং-সহায়তা সত্যিই শক্তিশালী।
  • তবু সব কিছু AI-এর উপর ছেড়ে দিলেই হয়নি। সামগ্রিক নকশা সমন্বয় করা, ভুল বোঝাবুঝি ঠিক করা এবং ট্রাবলশুটিংয়ের দিক নির্দেশ করা—একজন প্লেয়িং ম্যানেজারের মত ভূমিকা নিতে হয়েছে।
  • সেই অভিজ্ঞতা AI যুগে কীভাবে বাঁচতে হবে সে দিক থেকেও শিক্ষা দিয়েছে।

ব্রাউজারেই সম্পন্ন হওয়া SVG এডিটরের বাস্তবায়ন ও স্পেসিফিকেশনের বিশ্লেষণ

SVG কী

SVG (Scalable Vector Graphics) হলো W3C কর্তৃক মান্য করা XML-ভিত্তিক ভেক্টর ইমেজ ফরম্যাট। PNG বা JPEG-এর মতো রাস্টার ছবির বিপরীতে, বড় বা ছোট করলেও গুণমান নষ্ট হয় না। উপরন্তু, SVG DOM-এর অংশ হিসেবে সরাসরি HTML নথিতে এম্বেড করা যায়, ফলে JavaScript ও CSS দিয়ে গতিশীলভাবে নিয়ন্ত্রণ ও স্টাইল পরিবর্তন সম্ভব। স্পেসিফিকেশন W3C SVG সুপারিশ অনুসরণ করে এবং আকার, টেক্সট, পথ, গ্রেডিয়েন্ট, ফিল্টারসহ বিস্তৃত অভিব্যক্তি সমর্থন করে।

SVG সাধারণত নিচের কাজে ব্যবহৃত হয়।

  • ওয়েব পেজে আইকন বা ডায়াগ্রাম দেখানো
  • ডায়াগ্রাম বা চার্টের গতিশীল তৈরি
  • ব্যবহারকারীর ইন্টারঅ্যাকশনসহ চিত্র আঁকার অ্যাপ্লিকেশন
  • মুদ্রণ বা UI ডিজাইনের উপকরণ তৈরি

এই SVG এডিটরের সারাংশ

এডিটরটি সম্পূর্ণভাবে ব্রাউজারে চলা ক্লায়েন্ট-সাইড টুল। ব্যবহারকারী আয়তক্ষেত্র বা উপবৃত্তের মতো মৌলিক আকারের পাশাপাশি তারকা, স্পিচ বেলুন, মেঘ, হৃদয়, এমনকি মুক্ত রেখা আঁকার জন্য পেন মোডও ব্যবহার করতে পারেন। প্রতিটি আকার সরানো, স্কেল করা ও ঘোরানো যায়; অ্যালাইনমেন্ট, স্ন্যাপ, গ্রুপিং ও লেয়ারের তালিকাও সমর্থিত। সম্পন্ন চিত্র SVG বা PNG—উভয় ফরম্যাটেই এক্সপোর্ট করা সম্ভব।

নিচে আমি কেমন করে SVG স্পেসিফিকেশনকে JavaScript-এ রূপান্তর করেছি এবং কোথায় কোথায় হোঁচট খেয়েছি তা বিস্তারিতভাবে তুলে ধরা হলো।

বাস্তবায়নে স্পেসিফিকেশন মানা এবং যেসব জায়গায় থমকে গেছি

আকার যোগ করা ও অ্যাট্রিবিউট পরিচালনা

SVG স্পেসিফিকেশন অনুযায়ী প্রতিটি আকারের জন্য নির্দিষ্ট উপাদান ও অ্যাট্রিবিউট রয়েছে। আয়তক্ষেত্রের জন্য <rect>, উপবৃত্তের জন্য <ellipse>, বহুভুজের জন্য <polygon> ব্যবহার করেছি এবং যথাযথভাবে x, y, width, height, cx, cy, rx, ry, points ইত্যাদি অ্যাট্রিবিউট সেট করতে হয়েছে। তারকা বা হৃদয়ের মতো জটিল আকার <path> উপাদান দিয়ে তৈরি করেছি, যেখানে বেজিয়ার কার্ভ বা আর্ক কমান্ড মিলিয়ে নেওয়া হয়েছে।

বাস্তবায়ন উদাহরণ (আয়তক্ষেত্র যোগ করা)

function addRectangle(x, y, width, height) {
  const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  rect.setAttribute("x", x);
  rect.setAttribute("y", y);
  rect.setAttribute("width", width);
  rect.setAttribute("height", height);
  rect.setAttribute("fill", "transparent");
  rect.setAttribute("stroke", "#000");
  svg.appendChild(rect);
}

আয়তক্ষেত্র বা উপবৃত্ত সহজ, কিন্তু বহুভুজ বা কাস্টম শেপের ক্ষেত্রে কোঅর্ডিনেট হিসাব করতে হয়। উদাহরণস্বরূপ, তারকা আঁকার সময় কেন্দ্রীয় কোণ সমান ভাগে ভাগ করে শীর্ষবিন্দু বের করে points অ্যাট্রিবিউটে যোগ করেছি।

function createStar(cx, cy, spikes, outerR, innerR) {
  let points = "";
  const step = Math.PI / spikes;
  for (let i = 0; i < 2 * spikes; i++) {
    const r = i % 2 === 0 ? outerR : innerR;
    const x = cx + r * Math.cos(i * step);
    const y = cy + r * Math.sin(i * step);
    points += `${x},${y} `;
  }
  const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
  polygon.setAttribute("points", points.trim());
  polygon.setAttribute("stroke", "#000");
  polygon.setAttribute("fill", "transparent");
  svg.appendChild(polygon);
}

ট্রান্সফর্ম ও কোঅর্ডিনেট সিস্টেমের ব্যবস্থাপনা

SVG-তে transform অ্যাট্রিবিউটের মাধ্যমে translate, rotate, scale ইত্যাদি একত্র করে আকার নিয়ন্ত্রণ করা যায়। সমস্যার জায়গা ছিল নির্বাচিত আকারের বাউন্ডিং বক্স বের করার সময় লোকাল কোঅর্ডিনেট ও গ্লোবাল কোঅর্ডিনেটের পার্থক্য মেলানো। এর জন্য getScreenCTM() ব্যবহার করে পয়েন্টগুলোকে গ্লোবাল কোঅর্ডিনেটে রূপান্তর করেছি।

বাস্তবায়ন উদাহরণ (কোঅর্ডিনেট রূপান্তর প্রয়োগ)

function getTransformedBBox(element) {
  const bbox = element.getBBox();
  const matrix = element.getScreenCTM();
  const points = [
    svg.createSVGPoint(), svg.createSVGPoint(),
    svg.createSVGPoint(), svg.createSVGPoint()
  ];
  points[0].x = bbox.x; points[0].y = bbox.y;
  points[1].x = bbox.x + bbox.width; points[1].y = bbox.y;
  points[2].x = bbox.x; points[2].y = bbox.y + bbox.height;
  points[3].x = bbox.x + bbox.width; points[3].y = bbox.y + bbox.height;
  return points.map(p => p.matrixTransform(matrix));
}

এতে করে ঘোরানো বা স্কেল করার পরেও সঠিক নির্বাচন ফ্রেম আঁকা সম্ভব হয়েছে।

স্ন্যাপ ফিচার ও গ্রিড

গ্রিড আঁকতে SVG-এর <pattern> উপাদান ব্যবহার করেছি এবং ব্যাকগ্রাউন্ডে প্রয়োগ করেছি। স্ন্যাপ লজিক JavaScript-এ হিসাব করে নির্দিষ্ট ব্যবধানে মান রাউন্ড করার মাধ্যমে কাজ করে।

function snapToGrid(value, gridSize) {
  return Math.round(value / gridSize) * gridSize;
}

অবজেক্ট স্ন্যাপের ক্ষেত্রে অন্যান্য আকারের কেন্দ্র বা প্রান্ত স্ক্যান করে নিকটতম বিন্দু বের করেছি এবং সহায়ক রেখা দেখিয়ে স্ন্যাপ করিয়েছি। এতে UI-এর নির্ভুলতা অনেক বেড়েছে।

টেক্সট সম্পাদনা

SVG-এর <text> উপাদান সরাসরি সম্পাদনাযোগ্য নয়, তাই ইনলাইন সম্পাদনার জন্য HTML <input> সাময়িকভাবে ওভারলে করেছি।

function editTextElement(textElem) {
  const input = document.createElement("input");
  input.type = "text";
  input.value = textElem.textContent;
  document.body.appendChild(input);
  const bbox = textElem.getBoundingClientRect();
  input.style.position = "absolute";
  input.style.left = bbox.x + "px";
  input.style.top = bbox.y + "px";
  input.focus();
  input.addEventListener("blur", () => {
    textElem.textContent = input.value;
    document.body.removeChild(input);
  });
}

পথ ও মুক্ত রেখা

SVG-এর <path> উপাদান M, L, C, A ইত্যাদি কমান্ড মিলিয়ে আঁকা হয়। প্রথমে সরল রেখা কেন্দ্রিক সম্পাদনা দিয়ে শুরু করেছি; প্রতিটি ক্লিকে কোঅর্ডিনেট যোগ করে পথ বড়িয়েছি।

let pathData = "M100,100";
function addPathPoint(x, y) {
  pathData += ` L${x},${y}`;
  path.setAttribute("d", pathData);
}

আউটপুট (SVG/PNG এক্সপোর্ট)

সংরক্ষণের আগে সহায়ক উপাদান বাদ দিয়ে এক্সপোর্ট করেছি। PNG-তে রূপান্তরের জন্য Canvas ব্যবহার করেছি।

function exportToPNG(svgElement) {
  const serializer = new XMLSerializer();
  const source = serializer.serializeToString(svgElement);
  const img = new Image();
  const svgBlob = new Blob([source], { type: "image/svg+xml;charset=utf-8" });
  const url = URL.createObjectURL(svgBlob);
  img.onload = () => {
    const canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    canvas.toBlob(blob => {
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = "drawing.png";
      a.click();
    });
  };
  img.src = url;
}

ক্যানভাস স্ক্রোল সমর্থন

বড় SVG নিয়ে কাজ করার জন্য <div style="overflow:auto"> দিয়ে ক্যানভাস মোড়ানো হয়েছে, প্রয়োজন হলে স্ক্রোলবার দেখিয়ে সমস্যা মেটানো যায়।

সারসংক্ষেপ

SVG শক্তিশালী স্পেসিফিকেশন হলেও বাস্তবায়নে কোঅর্ডিনেট রূপান্তর বা পথ সংজ্ঞার মতো বেশ কিছু উচ্চ বাধা থাকে। এই এডিটরে সেগুলো গোছানো হয়েছে এবং নিচের সুবিধা নিশ্চিত করা হয়েছে।

  • আকার উপাদানের অ্যাট্রিবিউট নিয়ন্ত্রণ (rect, ellipse, polygon, path)
  • transform ও কোঅর্ডিনেট সিস্টেম সঠিকভাবে সামলানো
  • গ্রিড ও স্ন্যাপ বাস্তবায়ন
  • ইনলাইন টেক্সট সম্পাদনা
  • পথ সম্পাদনা (সরল রেখা ভিত্তিক)
  • PNG ও SVG—উভয় ফরম্যাটে এক্সপোর্ট
  • স্ক্রোলযোগ্য বড় ক্যানভাস সমর্থন

SVG-এর অভিব্যক্তিশক্তি কাজে লাগিয়ে ব্রাউজারের মধ্যে চলা উচ্চ-ক্ষমতাসম্পন্ন এডিটর তৈরি করা গেছে।