簡體   English   中英

將草圖縮放到不同的分辨率

[英]Scaling sketches to different resolutions

現在這似乎是一個愚蠢的問題,但請聽我說完。

直到此時,每當我想創建一個可以在多個分辨率上運行的草圖時,我都會放置某些對象,比如在width/2, height/2, width/16, width/16上放置一個ellipse()來制作一個圓圈(是的,我知道有一個圓圈 function 但這不是重點)。

但是,當創建包含一些細節的更精確的對象時,必須執行width/2.489或使用width/2 - someNumber可以實現相同效果時會非常煩人

假設我正在繪制Duck object 我想在xy顯示我希望能夠擁有Duckdraw()方法列表,如circle(x,y, 100); circle(x + someNum, y, 25); circle(x,y, 100); circle(x + someNum, y, 25);

我可以選擇標准尺寸,例如int STD = width / 100 ,然后通過執行諸如circle(x + STD * 3, y, 25); 但這也感覺不雅。

我知道 Processing 有一個scale()選項,但不幸的是沒有辦法只縮放正在繪制的對象的heightswidths 它也可以縮放 position 的東西。

所以,如果我先在size(1920/2 * 1080/2)上畫一個草圖,然后在width / 2, height / 2上畫一個圓圈,然后在size(1920,1080)scale(2)上運行草圖將以width / 2 * 2繪制圓圈並將圓圈幾乎從屏幕上移開

那么......為不同分辨率繪制詳細對象的最優雅方法是什么? (但相同的縱橫比)

幾年前我做過類似的事情。 我的太復雜了,不能只分享代碼,但我很樂意分享原理。

為了實現這一點,我將我想在草圖中展示的所有內容都建立在Drawable基礎 class 上,並使用Render方法來繪制它們。 由於所有東西都將繼承這種“一刀切”的方法,因此我可以實現一種在繪制時調整大小和替換所有東西的方法。

所以我決定了一個“官方”分辨率,這是一切都是真實的比例,在繪制時我的基礎 class 將糾正每個對象的位置和比例,我不必做一次該死的事情該部分已被編碼一次。

這是此邏輯的非常簡化的實現:

我們從一個全局變量開始,以相同的方式擴展所有內容:

final float scaleNum = 3.0;

我使用了一個漂亮的 ENUM 來決定(尚未編寫的)Drawable class 將如何選擇如何渲染 Drawable。 為此,您必須在fileName.java文件中聲明枚舉,因為處理不喜歡枚舉,因此您必須使用 java 來處理這個。 如果你不想這樣做,你可以使用其他任何東西,比如一個非常簡單的字符串,如果你願意的話。 這是我的枚舉:

//In Enums.java
enum Shape
{
  RECTANGLE,
  TRIANGLE,
  ELLIPSE
};

然后我們為所有可繪制對象編寫一個基礎 class。 這一個旨在僅處理少數操作可能性,但您似乎能夠輕松地整理出這種細節。

class Drawable {
  PVector position;
  boolean isVisible;
  float w, h;
  color fill = color(255,255,255);
  color stroke = color(255,255,255);
  float strokeWeight = 1;
  int shape;

  public Drawable (int shape) {
    this.shape = shape;
    position = new PVector(0,0);
    isVisible = true;
  }

  public void Render(){
    if (isVisible) {
      stroke(this.stroke);
      strokeWeight(this.strokeWeight);
      fill(this.fill);
      switch (shape) {
        case RECTANGLE:
          rect(position.x * scaleNum , position.y * scaleNum , w * scaleNum , h * scaleNum );
          break;
        case TRIANGLE:
          //code to draw a triangle at scale
          break;
        case ELLIPSE:
          //code to draw an ellipsis at scale
          break;
      }        
    }
  }
}

現在,這是一個繼承Drawable class 的 class 的示例:

class Paddle extends Drawable{
  PVector speed; // some class-specific variable

  Paddle (float positionX, float positionY) {
    super(RECTANGLE);  // this calls the parent class Constructor

    speed = new PVector(5, 5); //initializing stuff
    position = new PVector (positionX, positionY);
  }

  public void Move() { //class-specific method. Notice how it doesn't need to be scaled.
    position.x += speed.x;
    position.y += speed.y;
  }
}

現在,作為一項額外功能,由於所有可繪制對象都繼承了相同的可繪制對象Drawable ,因此您可以創建所有可繪制對象的列表,並將您想要繪制的任何內容添加到其中並讓它自己整理,而不是對程序中的所有內容進行微觀管理。 我的意思是,如果你有很多東西要畫,那就太棒了:

ArrayList <Drawable> myDrawables;

void setup () {
  myDrawables = new Arraylist <Drawables>;
  myDrawable.add(new Paddle(0, 0));
  Hero myHeroicCharacter = new Hero();
  myDrawables.add(myHeroicCharacter);
  myDrawable.add(new anotherClass(someVariables));
  //etc.  
}

void draw () {
  for (Drawable drawable : myDrawables) {
    drawable.Render();
  }
}

我不認為這在客觀上是最好的方法,但它肯定是一種非常簡單且易於管理的方法。 玩得開心!

您的目標是將任意坐標(我將其稱為在“世界空間”中)和 map 將它們帶到“屏幕空間”(顯示器上的像素)。 這樣做的最好方法確實是使用scale() 但是,在您的代碼中,您使用了scale(2) (從世界空間映射到更大的世界空間),然后您在屏幕空間坐標處繪制了width / 2, height / 2 (通常位於屏幕中間空間,但由於您之前按 2 縮放,因此您的width / 2height / 2正乘以 2)。

相反,您應該使用scale()到 map 從世界空間到屏幕空間,並使用世界空間坐標繪制對象。 例子:

// World space of 400 x 400, chosen arbitrarily
final float worldWidth = 400.0
  , worldHeight = 400.0;

void draw() {
  // Map the world space (of 400 x 400) to screen space
  /* Another way of thinking about this:
       A value on the X scale of 0 to 400 is divided by 400 into the scale of 0.0 to 1.0,
       and then is multiplied by the actual width of the screen;
       Whatever fraction of the world space's width it was at,
       it's now at that same fraction of the screen space's width.
  */
  scale(width/worldWidth, height/worldHeight);

  // Convert mouse coordinates from screen space to world space
  /* Explanation:
   We need to convert mouse coordinates from screen space to world space
   if we want to use their values in world space (scale only affects drawing).
   mouseX and mouseY are only changed when the mouse is moved;
   If we converted on frames where they haven't been updated,
   the conversions would accumulate.
   pmouseX and pmouseY are set to mouseX and mouseY between draw() calls.
   */
  if (pmouseX != mouseX || pmouseY != mouseY) {
    mouseX = round(mouseX * worldWidth / width);
    mouseY = round(mouseY * worldHeight / height);
  }

  // Example drawing code:
  background(64);
  // Same as ellipse(width/2, height/2, width/16, height/16) when not scaling
  ellipse(200, 200, 25, 25);
  // Same as ellipse(mouseX, mouseY, width/16, height/16) when not scaling
  ellipse(mouseX, mouseY, 25, 25);
}

如果您想校正屏幕比例,以便當您繪制相同寬度和高度的東西時,無論您的屏幕比例如何,它都會顯示為正方形,您可以執行以下操作:

// World space of 1600 x 900 (16:9), chosen arbitrarily
final float worldWidth = 1600.0
  , worldHeight = 900.0;

void draw() {
  // Uniformly scale the world space so that it just fits the screen
  float toScreenScale = min(width/worldWidth, height/worldHeight);
  // Or: Uniformly scale the world space so that the screen just fits inside it
  // float toScreenScale = max(width/worldWidth, height/worldHeight);
  // Draw in the middle of the borders (when using min for toScreenScale)
  float xBorder = (width - worldWidth * toScreenScale) / 2.0;
  float yBorder = (height - worldHeight * toScreenScale) / 2.0;
  translate(xBorder, yBorder);
  // Scale world space uniformly
  scale(toScreenScale);

  // Convert mouse coordinates from screen space to world space
  if (pmouseX != mouseX || pmouseY != mouseY) {
    mouseX = round( (mouseX - xBorder) / toScreenScale );
    mouseY = round( (mouseY - yBorder) / toScreenScale );
  }

  // Example drawing code:
  background(64);
  stroke(0);
  fill(255);
  ellipse(800, 450, 56.25, 56.25);
  ellipse(mouseX, mouseY, 56.25, 56.25);

  // Draw border to avoid spill-out (when using min for toScreenScale)
  noStroke();
  fill(0);
  resetMatrix();
  // Top
  rect(0, 0, width, yBorder);
  // Bottom
  rect(0, height, width, -yBorder);
  // Left
  rect(0, 0, xBorder, height);
  // Right
  rect(width, 0, -xBorder, height);
}

如果您的世界空間和屏幕空間的比例不同,這將為您提供邊界。 如果您不手動填充這些邊框(使用rect()您繪制完其他所有內容之后),事情可能會溢出到邊框中。 如果您將max用於toScreenScale ,它將切斷東西而不是給它們加邊框。


筆記:

  • 當我們將mouseXmouseY到比屏幕空間大的世界空間時,四舍五入會使鼠標坐標不准確; 相反,您最好制作一對新的float坐標(或PVector ),但您需要用該新變量替換代碼中所有出現的mouseXmouseY

沒有愚蠢的問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM