প্রসেসিং
হালনাগাদ! 4096x4096 চিত্র!
আমি দুটি প্রোগ্রাম একত্রিত করে আমার দ্বিতীয় পোস্টটিকে এটির সাথে একীভূত করেছি।
নির্বাচিত চিত্রগুলির একটি সম্পূর্ণ সংগ্রহটি ড্রপবক্সে পাওয়া যাবে । (দ্রষ্টব্য: ড্রপবক্স 4096x4096 চিত্রের জন্য পূর্বরূপ তৈরি করতে পারে না; কেবল তাদের ক্লিক করুন এবং "ডাউনলোড" ক্লিক করুন)।
আপনি যদি কেবলমাত্র এটির দিকে তাকান (টাইলিবল)! এখানে এটি ছোট করে দেওয়া হয়েছে (এবং আরও অনেক নীচে), আসল 2048x1024:
এই প্রোগ্রামটি রঙীন কিউবে এলোমেলোভাবে নির্বাচিত পয়েন্টগুলি থেকে পথ চলার মাধ্যমে কাজ করে, তারপরে এগুলি চিত্রের এলোমেলোভাবে নির্বাচিত পাথগুলিতে অঙ্কন করে। সম্ভাবনার অনেক আছে। কনফিগারযোগ্য বিকল্পগুলি হ'ল:
- রঙের কিউব পাথের সর্বাধিক দৈর্ঘ্য।
- রঙের ঘনক্ষেত্রের সর্বাধিক পদক্ষেপ নেওয়া (বৃহত্তর মানগুলি বৃহত্তর বৈচিত্রের কারণ ঘটায় তবে জিনিসগুলি শক্ত হয়ে গেলে শেষের দিকে ছোট ছোট পথগুলির সংখ্যা হ্রাস করুন)।
- ইমেজ টাইলিং।
- বর্তমানে দুটি চিত্রের মোড রয়েছে:
- মোড 1 (এই মূল পোস্টের মোড): চিত্রটিতে অব্যবহৃত পিক্সেলগুলির একটি ব্লক সন্ধান করে এবং সেই ব্লকে রেন্ডার করে। ব্লকগুলি হয় এলোমেলোভাবে অবস্থিত, বা বাম থেকে ডানে অর্ডার করা যেতে পারে।
- মোড 2 (আমার দ্বিতীয় পোস্টের মোড যা আমি এটির সাথে একীভূত করেছি): চিত্রটিতে একটি এলোমেলো সূচনা পয়েন্টটি বেছে নিয়েছে এবং অব্যবহৃত পিক্সেলের মাধ্যমে একটি পথ ধরে হাঁটছে; ব্যবহৃত পিক্সেল কাছাকাছি চলতে পারেন। এই মোডের জন্য বিকল্পগুলি:
- চলার জন্য দিকনির্দেশের সেট (অরথোগোনাল, তির্যক বা উভয়)।
- প্রতি পদক্ষেপের পরে দিকটি পরিবর্তন করতে (বর্তমানে ঘড়ির কাঁটার দিকে কিন্তু কোডটি নমনীয়) বা না হওয়া, বা কেবল কোনও দখল পিক্সেলের মুখোমুখি হওয়ার পরে দিক পরিবর্তন করতে হবে কিনা Whether
- দিক পরিবর্তনের ক্রম সাফ করার বিকল্প (ঘড়ির কাঁটার পরিবর্তে)।
এটি 4096x4096 অবধি সমস্ত আকারের জন্য কাজ করে।
সম্পূর্ণ প্রসেসিং স্কেচটি এখানে পাওয়া যাবে: ট্রেসারআরজিপ
আমি কেবল স্থান বাঁচাতে নীচে একই কোড ব্লকের সমস্ত ফাইল পেস্ট করেছি (এমনকি একটি ফাইলের মধ্যে এটি এখনও একটি বৈধ স্কেচ)। আপনি যদি একটি প্রিসেট ব্যবহার করতে চান তবে gPreset
অ্যাসাইনমেন্টে সূচি পরিবর্তন করুন । আপনি যদি প্রসেসিংয়ে r
এটি চালনা করেন তবে আপনি এটি চাপতে পারবেন যখন এটি একটি নতুন চিত্র তৈরি করার জন্য চলছে।
- আপডেট 1: প্রথম অব্যবহৃত রঙ / পিক্সেল এবং ज्ञात-ব্যবহৃত পিক্সেলগুলির উপরে অনুসন্ধান না করার জন্য অনুকূলিত কোড; 20-30x1024 প্রজন্মের সময় 10-30 মিনিট থেকে প্রায় 15 সেকেন্ডে এবং 4096x4096 1-3 ঘন্টা থেকে প্রায় 1 মিনিটে হ্রাস পেয়েছে। ড্রপ বক্স উত্স এবং উত্স নীচে আপডেট হয়েছে।
- আপডেট 2: স্থির ত্রুটি যা 4096x4096 চিত্র তৈরি হতে বাধা দিচ্ছিল।
final int BITS = 5; // Set to 5, 6, 7, or 8!
// Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts)
final Preset[] PRESETS = new Preset[] {
// 0
new Preset("flowers", BITS, 8*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS),
new Preset("diamonds", BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
new Preset("diamondtile", BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
new Preset("shards", BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS | ImageRect.SHUFFLE_DIRS),
new Preset("bigdiamonds", BITS, 100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
// 5
new Preset("bigtile", BITS, 100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
new Preset("boxes", BITS, 32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW),
new Preset("giftwrap", BITS, 32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.WRAP),
new Preset("diagover", BITS, 32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW),
new Preset("boxfade", BITS, 32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW | ImageRect.CHANGE_DIRS),
// 10
new Preset("randlimit", BITS, 512, 2, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
new Preset("ordlimit", BITS, 64, 2, ImageRect.MODE1, 0),
new Preset("randtile", BITS, 2048, 3, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS | ImageRect.WRAP),
new Preset("randnolimit", BITS, 1000000, 1, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
new Preset("ordnolimit", BITS, 1000000, 1, ImageRect.MODE1, 0)
};
PGraphics gFrameBuffer;
Preset gPreset = PRESETS[2];
void generate () {
ColorCube cube = gPreset.createCube();
ImageRect image = gPreset.createImage();
gFrameBuffer = createGraphics(gPreset.getWidth(), gPreset.getHeight(), JAVA2D);
gFrameBuffer.noSmooth();
gFrameBuffer.beginDraw();
while (!cube.isExhausted())
image.drawPath(cube.nextPath(), gFrameBuffer);
gFrameBuffer.endDraw();
if (gPreset.getName() != null)
gFrameBuffer.save(gPreset.getName() + "_" + gPreset.getCubeSize() + ".png");
//image.verifyExhausted();
//cube.verifyExhausted();
}
void setup () {
size(gPreset.getDisplayWidth(), gPreset.getDisplayHeight());
noSmooth();
generate();
}
void keyPressed () {
if (key == 'r' || key == 'R')
generate();
}
boolean autogen = false;
int autop = 0;
int autob = 5;
void draw () {
if (autogen) {
gPreset = new Preset(PRESETS[autop], autob);
generate();
if ((++ autop) >= PRESETS.length) {
autop = 0;
if ((++ autob) > 8)
autogen = false;
}
}
if (gPreset.isWrapped()) {
int hw = width/2;
int hh = height/2;
image(gFrameBuffer, 0, 0, hw, hh);
image(gFrameBuffer, hw, 0, hw, hh);
image(gFrameBuffer, 0, hh, hw, hh);
image(gFrameBuffer, hw, hh, hw, hh);
} else {
image(gFrameBuffer, 0, 0, width, height);
}
}
static class ColorStep {
final int r, g, b;
ColorStep (int rr, int gg, int bb) { r=rr; g=gg; b=bb; }
}
class ColorCube {
final boolean[] used;
final int size;
final int maxPathLength;
final ArrayList<ColorStep> allowedSteps = new ArrayList<ColorStep>();
int remaining;
int pathr = -1, pathg, pathb;
int firstUnused = 0;
ColorCube (int size, int maxPathLength, int maxStep) {
this.used = new boolean[size*size*size];
this.remaining = size * size * size;
this.size = size;
this.maxPathLength = maxPathLength;
for (int r = -maxStep; r <= maxStep; ++ r)
for (int g = -maxStep; g <= maxStep; ++ g)
for (int b = -maxStep; b <= maxStep; ++ b)
if (r != 0 && g != 0 && b != 0)
allowedSteps.add(new ColorStep(r, g, b));
}
boolean isExhausted () {
println(remaining);
return remaining <= 0;
}
boolean isUsed (int r, int g, int b) {
if (r < 0 || r >= size || g < 0 || g >= size || b < 0 || b >= size)
return true;
else
return used[(r*size+g)*size+b];
}
void setUsed (int r, int g, int b) {
used[(r*size+g)*size+b] = true;
}
int nextColor () {
if (pathr == -1) { // Need to start a new path.
// Limit to 50 attempts at random picks; things get tight near end.
for (int n = 0; n < 50 && pathr == -1; ++ n) {
int r = (int)random(size);
int g = (int)random(size);
int b = (int)random(size);
if (!isUsed(r, g, b)) {
pathr = r;
pathg = g;
pathb = b;
}
}
// If we didn't find one randomly, just search for one.
if (pathr == -1) {
final int sizesq = size*size;
final int sizemask = size - 1;
for (int rgb = firstUnused; rgb < size*size*size; ++ rgb) {
pathr = (rgb/sizesq)&sizemask;//(rgb >> 10) & 31;
pathg = (rgb/size)&sizemask;//(rgb >> 5) & 31;
pathb = rgb&sizemask;//rgb & 31;
if (!used[rgb]) {
firstUnused = rgb;
break;
}
}
}
assert(pathr != -1);
} else { // Continue moving on existing path.
// Find valid next path steps.
ArrayList<ColorStep> possibleSteps = new ArrayList<ColorStep>();
for (ColorStep step:allowedSteps)
if (!isUsed(pathr+step.r, pathg+step.g, pathb+step.b))
possibleSteps.add(step);
// If there are none end this path.
if (possibleSteps.isEmpty()) {
pathr = -1;
return -1;
}
// Otherwise pick a random step and move there.
ColorStep s = possibleSteps.get((int)random(possibleSteps.size()));
pathr += s.r;
pathg += s.g;
pathb += s.b;
}
setUsed(pathr, pathg, pathb);
return 0x00FFFFFF & color(pathr * (256/size), pathg * (256/size), pathb * (256/size));
}
ArrayList<Integer> nextPath () {
ArrayList<Integer> path = new ArrayList<Integer>();
int rgb;
while ((rgb = nextColor()) != -1) {
path.add(0xFF000000 | rgb);
if (path.size() >= maxPathLength) {
pathr = -1;
break;
}
}
remaining -= path.size();
//assert(!path.isEmpty());
if (path.isEmpty()) {
println("ERROR: empty path.");
verifyExhausted();
}
return path;
}
void verifyExhausted () {
final int sizesq = size*size;
final int sizemask = size - 1;
for (int rgb = 0; rgb < size*size*size; ++ rgb) {
if (!used[rgb]) {
int r = (rgb/sizesq)&sizemask;
int g = (rgb/size)&sizemask;
int b = rgb&sizemask;
println("UNUSED COLOR: " + r + " " + g + " " + b);
}
}
if (remaining != 0)
println("REMAINING COLOR COUNT IS OFF: " + remaining);
}
}
static class ImageStep {
final int x;
final int y;
ImageStep (int xx, int yy) { x=xx; y=yy; }
}
static int nmod (int a, int b) {
return (a % b + b) % b;
}
class ImageRect {
// for mode 1:
// one of ORTHO_CW, DIAG_CW, ALL_CW
// or'd with flags CHANGE_DIRS
static final int ORTHO_CW = 0;
static final int DIAG_CW = 1;
static final int ALL_CW = 2;
static final int DIR_MASK = 0x03;
static final int CHANGE_DIRS = (1<<5);
static final int SHUFFLE_DIRS = (1<<6);
// for mode 2:
static final int RANDOM_BLOCKS = (1<<0);
// for both modes:
static final int WRAP = (1<<16);
static final int MODE1 = 0;
static final int MODE2 = 1;
final boolean[] used;
final int width;
final int height;
final boolean changeDir;
final int drawMode;
final boolean randomBlocks;
final boolean wrap;
final ArrayList<ImageStep> allowedSteps = new ArrayList<ImageStep>();
// X/Y are tracked instead of index to preserve original unoptimized mode 1 behavior
// which does column-major searches instead of row-major.
int firstUnusedX = 0;
int firstUnusedY = 0;
ImageRect (int width, int height, int drawMode, int drawOpts) {
boolean myRandomBlocks = false, myChangeDir = false;
this.used = new boolean[width*height];
this.width = width;
this.height = height;
this.drawMode = drawMode;
this.wrap = (drawOpts & WRAP) != 0;
if (drawMode == MODE1) {
myRandomBlocks = (drawOpts & RANDOM_BLOCKS) != 0;
} else if (drawMode == MODE2) {
myChangeDir = (drawOpts & CHANGE_DIRS) != 0;
switch (drawOpts & DIR_MASK) {
case ORTHO_CW:
allowedSteps.add(new ImageStep(1, 0));
allowedSteps.add(new ImageStep(0, -1));
allowedSteps.add(new ImageStep(-1, 0));
allowedSteps.add(new ImageStep(0, 1));
break;
case DIAG_CW:
allowedSteps.add(new ImageStep(1, -1));
allowedSteps.add(new ImageStep(-1, -1));
allowedSteps.add(new ImageStep(-1, 1));
allowedSteps.add(new ImageStep(1, 1));
break;
case ALL_CW:
allowedSteps.add(new ImageStep(1, 0));
allowedSteps.add(new ImageStep(1, -1));
allowedSteps.add(new ImageStep(0, -1));
allowedSteps.add(new ImageStep(-1, -1));
allowedSteps.add(new ImageStep(-1, 0));
allowedSteps.add(new ImageStep(-1, 1));
allowedSteps.add(new ImageStep(0, 1));
allowedSteps.add(new ImageStep(1, 1));
break;
}
if ((drawOpts & SHUFFLE_DIRS) != 0)
java.util.Collections.shuffle(allowedSteps);
}
this.randomBlocks = myRandomBlocks;
this.changeDir = myChangeDir;
}
boolean isUsed (int x, int y) {
if (wrap) {
x = nmod(x, width);
y = nmod(y, height);
}
if (x < 0 || x >= width || y < 0 || y >= height)
return true;
else
return used[y*width+x];
}
boolean isUsed (int x, int y, ImageStep d) {
return isUsed(x + d.x, y + d.y);
}
void setUsed (int x, int y) {
if (wrap) {
x = nmod(x, width);
y = nmod(y, height);
}
used[y*width+x] = true;
}
boolean isBlockFree (int x, int y, int w, int h) {
for (int yy = y; yy < y + h; ++ yy)
for (int xx = x; xx < x + w; ++ xx)
if (isUsed(xx, yy))
return false;
return true;
}
void drawPath (ArrayList<Integer> path, PGraphics buffer) {
if (drawMode == MODE1)
drawPath1(path, buffer);
else if (drawMode == MODE2)
drawPath2(path, buffer);
}
void drawPath1 (ArrayList<Integer> path, PGraphics buffer) {
int w = (int)(sqrt(path.size()) + 0.5);
if (w < 1) w = 1; else if (w > width) w = width;
int h = (path.size() + w - 1) / w;
int x = -1, y = -1;
int woff = wrap ? 0 : (1 - w);
int hoff = wrap ? 0 : (1 - h);
// Try up to 50 times to find a random location for block.
if (randomBlocks) {
for (int n = 0; n < 50 && x == -1; ++ n) {
int xx = (int)random(width + woff);
int yy = (int)random(height + hoff);
if (isBlockFree(xx, yy, w, h)) {
x = xx;
y = yy;
}
}
}
// If random choice failed just search for one.
int starty = firstUnusedY;
for (int xx = firstUnusedX; xx < width + woff && x == -1; ++ xx) {
for (int yy = starty; yy < height + hoff && x == -1; ++ yy) {
if (isBlockFree(xx, yy, w, h)) {
firstUnusedX = x = xx;
firstUnusedY = y = yy;
}
}
starty = 0;
}
if (x != -1) {
for (int xx = x, pathn = 0; xx < x + w && pathn < path.size(); ++ xx)
for (int yy = y; yy < y + h && pathn < path.size(); ++ yy, ++ pathn) {
buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
setUsed(xx, yy);
}
} else {
for (int yy = 0, pathn = 0; yy < height && pathn < path.size(); ++ yy)
for (int xx = 0; xx < width && pathn < path.size(); ++ xx)
if (!isUsed(xx, yy)) {
buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
setUsed(xx, yy);
++ pathn;
}
}
}
void drawPath2 (ArrayList<Integer> path, PGraphics buffer) {
int pathn = 0;
while (pathn < path.size()) {
int x = -1, y = -1;
// pick a random location in the image (try up to 100 times before falling back on search)
for (int n = 0; n < 100 && x == -1; ++ n) {
int xx = (int)random(width);
int yy = (int)random(height);
if (!isUsed(xx, yy)) {
x = xx;
y = yy;
}
}
// original:
//for (int yy = 0; yy < height && x == -1; ++ yy)
// for (int xx = 0; xx < width && x == -1; ++ xx)
// if (!isUsed(xx, yy)) {
// x = xx;
// y = yy;
// }
// optimized:
if (x == -1) {
for (int n = firstUnusedY * width + firstUnusedX; n < used.length; ++ n) {
if (!used[n]) {
firstUnusedX = x = (n % width);
firstUnusedY = y = (n / width);
break;
}
}
}
// start drawing
int dir = 0;
while (pathn < path.size()) {
buffer.set(nmod(x, width), nmod(y, height), path.get(pathn ++));
setUsed(x, y);
int diro;
for (diro = 0; diro < allowedSteps.size(); ++ diro) {
int diri = (dir + diro) % allowedSteps.size();
ImageStep step = allowedSteps.get(diri);
if (!isUsed(x, y, step)) {
dir = diri;
x += step.x;
y += step.y;
break;
}
}
if (diro == allowedSteps.size())
break;
if (changeDir)
++ dir;
}
}
}
void verifyExhausted () {
for (int n = 0; n < used.length; ++ n)
if (!used[n])
println("UNUSED IMAGE PIXEL: " + (n%width) + " " + (n/width));
}
}
class Preset {
final String name;
final int cubeSize;
final int maxCubePath;
final int maxCubeStep;
final int imageWidth;
final int imageHeight;
final int imageMode;
final int imageOpts;
final int displayScale;
Preset (Preset p, int colorBits) {
this(p.name, colorBits, p.maxCubePath, p.maxCubeStep, p.imageMode, p.imageOpts);
}
Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts) {
final int csize[] = new int[]{ 32, 64, 128, 256 };
final int iwidth[] = new int[]{ 256, 512, 2048, 4096 };
final int iheight[] = new int[]{ 128, 512, 1024, 4096 };
final int dscale[] = new int[]{ 2, 1, 1, 1 };
this.name = name;
this.cubeSize = csize[colorBits - 5];
this.maxCubePath = maxCubePath;
this.maxCubeStep = maxCubeStep;
this.imageWidth = iwidth[colorBits - 5];
this.imageHeight = iheight[colorBits - 5];
this.imageMode = imageMode;
this.imageOpts = imageOpts;
this.displayScale = dscale[colorBits - 5];
}
ColorCube createCube () {
return new ColorCube(cubeSize, maxCubePath, maxCubeStep);
}
ImageRect createImage () {
return new ImageRect(imageWidth, imageHeight, imageMode, imageOpts);
}
int getWidth () {
return imageWidth;
}
int getHeight () {
return imageHeight;
}
int getDisplayWidth () {
return imageWidth * displayScale * (isWrapped() ? 2 : 1);
}
int getDisplayHeight () {
return imageHeight * displayScale * (isWrapped() ? 2 : 1);
}
String getName () {
return name;
}
int getCubeSize () {
return cubeSize;
}
boolean isWrapped () {
return (imageOpts & ImageRect.WRAP) != 0;
}
}
এখানে আমার পছন্দ মতো 256x128 চিত্রের সম্পূর্ণ সেট রয়েছে:
মোড 1:
মূল সেট থেকে আমার প্রিয় (সর্বোচ্চ_পথ_সাগরণ = 512, পাথ_স্টেপ = 2, এলোমেলো, 2x লিঙ্ক, 256x128 লিঙ্ক ):
অন্যগুলি (বাম দু'টি অর্ডার করা হয়েছে, ডান দুটি এলোমেলো, শীর্ষ দুটি পথের দৈর্ঘ্য সীমিত, নীচে দুটি অপসারণযুক্ত):
এটি একটি টাইল করা যেতে পারে:
মোড 2:
এইগুলি টাইল করা যেতে পারে:
512x512 নির্বাচন:
টাইলিবল হীরা, মোড 2 থেকে আমার প্রিয়; আপনি এইটিতে দেখতে পাচ্ছেন যে কীভাবে বিদ্যমান বস্তুর চারপাশে পথ চলতে পারে:
বৃহত্তর পাথ পদক্ষেপ এবং সর্বাধিক পাথ দৈর্ঘ্য, টাইলিবল:
এলোমেলো মোড 1, টাইলযোগ্য:
আরও নির্বাচন:
512x512 সমস্ত রেন্ডারিংয়ের ড্রপবক্স ফোল্ডারে (* _64.png) পাওয়া যাবে।
2048x1024 এবং 4096x4096:
এগুলি এম্বেড করার জন্য খুব বড় এবং আমি দেখতে পেয়েছি এমন সমস্ত চিত্র হোস্টগুলি এগুলি 1600x1200 এ নেমে গেছে। আমি বর্তমানে 4096x4096 চিত্রের একটি সেট রেন্ডার করছি তাই আরও শীঘ্রই পাওয়া যাবে। এখানে সমস্ত লিঙ্ক অন্তর্ভুক্ত করার পরিবর্তে, কেবল ড্রপবক্স ফোল্ডারে তাদের পরীক্ষা করে দেখুন (* _128.png এবং * _256.png, দ্রষ্টব্য: 4096x4096 টি ড্রপবক্স পূর্বরূপের জন্য খুব বড়, কেবল "ডাউনলোড" ক্লিক করুন)। যদিও আমার কয়েকটি প্রিয় এখানে রয়েছে:
2048x1024 বড় টাইলিবল হীরা (এই পোস্টের শুরুতে আমি একই সংযুক্ত করেছি)
2048x1024 হীরা (আমি এটিকে ভালবাসি!), ছোট করে:
4096x4096 বড় টাইলিবল হীরা (অবশেষে! ড্রপবক্স লিঙ্কে 'ডাউনলোড' ক্লিক করুন; এটি তাদের পূর্বরূপকারীর জন্য খুব বড়), নীচে রেখে ছোট করুন:
4096x4096 এলোমেলো মোড 1 :
4096x4096 আর একটি দুর্দান্ত
আপডেট: 2048x1024 প্রিসেট চিত্র সেটটি সমাপ্ত এবং ড্রপবক্সে। 4096x4096 সেটটি ঘন্টার মধ্যে করা উচিত।
বেশ কয়েকজন ভাল আছে, কোনটি পোস্ট করতে হবে তা বেছে নিতে আমার খুব কষ্ট হচ্ছে, সুতরাং দয়া করে ফোল্ডারটির লিঙ্কটি পরীক্ষা করে দেখুন!