Set size in cm/mm?

edited January 2018 in How To...

If I recall correctly all size parameters are in pixels. Is it possible, if one wants, to define them in terms of cm or mm? You would need the pixel density of the monitor, right? Is that possible to get in Processing?

As a side note, it is quite difficult to google solutions specific to Processing since the name is such a commonly used word. I suppose I should have done the site:processing.org thing..

Tagged:

Answers

  • Here's a bit of a hacky solution (however this approach only works if you redraw your screen every frame).

    Here we scale a 20x20 pixel grid up to a 200x200 pixel grid. If you need it in cm, you will need to get the screens DPI (pixels per inch) and convert it to cm. Average screen DPI I think is 72 and you can test whether the screen is high-dpi by calling https://processing.org/reference/displayDensity_.html

    void setup() {
      size(200,200);
    }
    
    void draw() {
      //you can draw everything you like on a smaller scale, say 20x20 pixels
      background(255); //background color
      int smallW = 20, smallH = 20;
      stroke(255,0,0);
      fill(0,0,255);
      rect(5,5,10,10);
    
      //and then read the pixels
      loadPixels();
      color[] pix = pixels;
      println(pix.length);
    
      //and then scale it up to 200x200
      background(255); //clear the screen
      noStroke();
      for(int i = 0; i < smallH; ++i) {
        for(int j = 0; j < smallW; ++j) {
          fill(pix[i*width+j]);
          rect((float)j/smallW * width, (float)i/smallH * height, 1./smallW * width, 1./smallH * height);
        }
      }
    }
    
  • edited January 2018

    In order to fix visual glitches, in case smallW is not a multiple of width you can use the following code (it basically ceils and floors the correct values):

    void setup() {
      size(200,200);  
    }
    
    void draw() {
      //you can draw everything you like on a smaller scale, say 20x20 pixels
      background(255); //background color
      int smallW = 21, smallH = 21;
      stroke(255,0,0);
      fill(0,0,255);
      point(0,0);
      point(20,20);
      rect(5,5,10,10);
    
      //and then read the pixels
      loadPixels();
      color[] pix = pixels;
      println(pix.length);
      //and then scale it up to 200x200
      background(255); //clear the screen
      noStroke();
      for(int i = 0; i < smallH; ++i) {
        for(int j = 0; j < smallW; ++j) {
          fill(pix[i*width+j]);
          rect(floor((float)j/smallW * width), floor((float)i/smallH * height), ceil(1./smallW * width), ceil(1./smallH * height));
        }
      }
    }
    
  • Average screen DPI I think is 72 and you can test whether the screen is high-dpi by calling https://processing.org/reference/displayDensity_.html

    Is DPI limited to a set of values? Or can it vary freely from monitor to monitor?

  • edited January 2018

    https://en.wikipedia.org/wiki/Dots_per_inch#Computer_monitor_DPI_standards

    This is OS specific, and device specific, and complex. You can get a general hint about being on a single or double density display to get you in the right ballpark, but you need to know more to say anything about the physical display size vs pixels (or scaled virtual pixels). If you don't know the actual physical screen dimensions, find the OS at a minimum.

  • Thanks for the answer, I've resolved the dpi problem. The program is going to run only on one computer, so I just used the screen size and the resolution to calculate the dpi. The problem is that the dpi was in decimals, 96.11 or something like this. Converting this to dots per mm gives 3.78 pixels per mm. Since height and width values are integers in the resize function, it seems that it is impossible to resize and image into something that is exactly, for example, 10 mm, since that would mean resizing that parameter to 37.8 pixels. Is there any way to obtain an exact resizing in terms of mm? Thanks.

  • edited January 2018

    You can't resize the window to 96.11, but you can absolute draw a PImage as 96.11 pixels wide. You can also position by fractions of a pixel. Floats are built in all the way down.

    That said, rounding to the nearest pixel is probably what you want to do for large objects -- I''m not sure how much 0.27 millimeters really matters on a 10mm image. For small ones, just give the correct fractional size, e.g.

    translate(96.1, 96.1); // move 1x1mm
    image(img, 0, 0, 961.1, 961.1); // display 10x10mm
    

    Note that this inline resizing form of image() is very processor intensive -- better to resize just once and then use a simple img() to display. You can also do this by drawing to a transparent PGraphics that is rounded up e.g. 2 pixels -- that leaves you space for an antialiasing edge and transparency.

  • I used this before when creating pdf's from processing:

      import processing.pdf.*;
    
    
    void settings() {
       size((int)mm(170),(int)mm(210)); 
    }
    
    
    void draw() {
      beginRecord(PDF, "output.pdf");
    
      strokeWeight(mm(3));
      rect(mm(10), mm(10), mm(150), mm(10));
    
      endRecord();
      exit();
    }
    
    
    
    static final float MM_TO_PIXEL_RATIO = 0.3527778f;
    
    public float mm(float wantedMM) {
      return wantedMM / MM_TO_PIXEL_RATIO;
    }
    

    When opening them in illustrator they have the correct size.

    Now if I still would need it I would use something like this:

    import processing.pdf.*;
    
    
    void settings() {
       size(170, 210); 
    }
    
    void draw() {
      beginRecord(PDF, "output.pdf");
    
      strokeWeight(3);
      rect(10, 10, 150, 10);
    
      endRecord();
      exit();
    }
    
    
    void size(int w, int h) {
      super.size(round(mm(w)), round(mm(h)));
    }
    
    void rect(float a, float b, float c, float d) {
      super.rect(mm(a), mm(b), mm(c), mm(d));
    }
    
    void strokeWeight(float s) {
       super.strokeWeight(mm(s)); 
    }
    
    static final float MM_TO_PIXEL_RATIO = 0.3527778f;
    
    public float mm(float wantedMM) {
      return wantedMM / MM_TO_PIXEL_RATIO;
    }
    

    Maybe even add an option so you can switch between using pixels and mm.

  • Note that this inline resizing form of image() is very processor intensive -- better to resize just once and then use a simple img() to display.

    This is basically what I am doing. The problem is that according to the IDE the resize function only takes int values.

  • edited January 2018

    Right -- that is true, because the output of resize() is a new pixel grid, so its width and height must be ints. So if you want a fractional resize:

    You can also do this by drawing to a transparent PGraphics that is rounded up e.g. 2 pixels -- that leaves you space for an antialiasing edge and transparency.

    You then draw -- once -- to this int-based PGraphics pg using float-based width and height, pg.image(img, 0, 0, floatwidth, floatheight) where floatwidth / floatheight are less than pg.width / pg.height (you rounded up when you created pg).

    Now you have a PGraphics image that is pre-resized to a fraction of a pixel. It has a soft edge on the right and/or bottom. You can further draw this image at a fractional pixel location -- for example with image(pg, 100.5, 100.5) -- without resizing.

Sign In or Register to comment.