bsansouci / reasongl

Reason bindings to WebGL and OpenGL.
107 stars 8 forks source link

Mouse/Touch co-ordinates are not based on canvas positions #15

Open RawToast opened 5 years ago

RawToast commented 5 years ago

The co-ordinates given for x/y mouse positions are not based on the actual canvas size. If the canvas size is increased (via CSS) then the values are based on the actual pixels and not canvas units.

Updating the mouse and touches sections as follows seems to resolve this; although, pixelWidthX/Y might be better candidates for this change.

let x = int_of_float(float_of_int(getClientX(e) - getLeft(rect)) /. Document.devicePixelRatio);
let y = int_of_float(float_of_int(getClientY(e) - getTop(rect)) /. Document.devicePixelRatio);

Edit: that's no good. It just happened to be closer as my canvas was ~x1.8 and my machines ratio is x2.

RawToast commented 5 years ago

After reflection I think this can be fixed rather nicely.

In other places you use a getLeft function, like so:

let rect = getBoundingClientRect(canvas);
 let x = getClientX(e) - getLeft(rect);

The rect also holds the actual width/height by adding methods like so:

[@bs.get] external getRectWidth : 'a => int = "width";

[@bs.get] external getRectHeight : 'a => int = "height";

Then the canvas details in style need to be fetched. One way is to grab them from the style in the canvas element, like so:

let style = getStyle(canvas);
let canvasWidth = canvas |> getStyle |> getWidthStyle |> s => Js.String.slice(0, Js.String.length(s) - 2, s) |> int_of_string;

Although, the style in canvas is passed in -- so I might be able to just store and access that value rather than read the dom.

This would let you compare the actual width with the one in Reprocessing and simply use a simple calculation: x => x (canvasWidth / rectWidth)

I'll try this out tonight and raise a PR if it's successful.

RawToast commented 5 years ago

A simple solution, just for mouseDown: https://github.com/RawToast/reasongl/blob/96ab27a508f31baa89b79578a7267d6b2dadaa83/src/web/reasongl_web.re#L340-L354

          let rect = getBoundingClientRect(canvas);
          let left = getLeft(rect);
          let top = getTop(rect);
          let actualWidth = getRectWidth(rect);
          let actualHeight = getRectHeight(rect);

          let style = getStyle(canvas);
          let removePx = s => Js.String.slice(0, Js.String.length(s) - 2, s) |> float_of_string;
          let width = style |> getWidthStyle |> removePx;
          let height = style |> getHeightStyle |> removePx;

          let state = Events.MouseDown;
          let x = int_of_float((float_of_int(getClientX(e) - left)) *. (width /. actualWidth));
          let y = int_of_float((float_of_int(getClientY(e) - top)) *. (width /. actualWidth));
          cb(~button, ~state, ~x, ~y);
Schmavery commented 5 years ago

Makes sense in general, we could add some helper function that does this kind of division and use it for all the mouse events, would be great to be able to fix this.

On the other hand, can we rely on the style.width and style.height? Could they be specified in values other than px? Or set indirectly using max-width or something?

RawToast commented 5 years ago

I think it is Reprocessing that sets those styles -- so it is rathe Reprocessing specific! I'd rather take it from the size given

Maybe add two Ref fields on initialisation and updating them on calls like windowResize would be a more generic implementation -- and more performant. In the unlikely event the Ref is None we could just not do the calculation.

Schmavery commented 5 years ago

Oh I see, I was looking at it backwards. You're basically pulling the values from here, right: https://github.com/bsansouci/reasongl/blob/868ca060c7fe41595fe6157c7ab668af82a170e1/src/web/reasongl_web.re#L234-L245

Seems like it could be ok in that case? Not sure what kind of css you're imagining to increase the canvas size.

RawToast commented 5 years ago

Yeah, that's where I get it. -- but I think a ref would be the better solution.

I already am manipulating the canvas, check it out The canvas is 568px by 320px, but via the all powerful !important I can stretch it 😆

 <style>
    * {
      margin: 0;
      padding: 0;
    }
    body {
      overflow: hidden;
    }
    #gameContainer canvas {
      display:block;
      width: 100% !important;
      height: 100% !important;
    }
    div#webgl-content {
      position: absolute;
      top: 50%;
      left: 50%;
      -webkit-transform: translate(-50%, -50%);
      transform: translate(-50%, -50%);
    }
</style>

  </head>
  <body style="display:flex;flex-wrap:wrap; background-color: #222; ">
      <div id="webgl-content" style="width:80%; height: 80%; min-height: 320px; max-height: 480px; min-width:568px; max-width:852px;">
          <div id="gameContainer" style="width: 100%; height: 100%">
              <canvas id="game" width="1136" height="640" style="background-color: black; width: 568px; height: 320px;"></canvas>
              <script src="main.js"></script>
          </div>
        </div>