Sunday, 16 September 2018

convert regular 2d rectangle coords to trapeze

I started to build a widget that uses svg asset that is a soccer court. I was working with regular 2d rectangle so far and it went well. However i wanted to replace that asset with this one:

enter image description here

I started to prototype on how to calculate the ball position in this kind of svg and its not going well. I guess that what i need is some kind of conversion from regular 2d rectangle model to something else that would account trapeze figure.

Maybe someone could help with understanding how its done. Lets say i have following coords {x: 0.2, y: 0.2} which means i have to put the ball in 20% of width of court and 20% of its height. How do i do in this example?

EDIT #1

I read an answer posted by MBo and i made effort to rewrite delphi code to JavaScript.I dont know delphi at all but i think it went well, however after trying code out i bumped onto couple of problems:

  1. trapeze is reversed (shorter horizotal line on the bottom), i attempted to fix it but without success, after couple of tries i had this as i wanted but then 0.2, 0.2 coord showed up on the bottom instead of closer to the top.

  2. i am not sure if the calculation works correctly in general, center coord seems odly gravitating towards bottom (at least it is my visual impresion)

  3. I attempted to fix reversed trapeze problem by playing with YShift = Hg / 4; but it causes variety of issues. Would like to know how this works exactly

  4. From what i understand, this script works in a way that you specify longer horizontal line Wd and height Hg and this produces a trapeze for you, is that correct?

this is working code:

function CalcAxialSymPersp(SrcWdt, SrcHgt, DstWdt, RBX, RBY, RTX, RTY /* Integer */) {
        var A, H, E; /* Double */
        
        A = (2 * RBX - DstWdt) / SrcWdt;
        H = (A * SrcWdt/ (2 * RTX - DstWdt) - 1) / SrcHgt;
        E = (RTY * (H * SrcHgt + 1) - RBY) / SrcHgt;
        
        return { A, H, E };
}

function PerspMap(SrcWdt, DstHgt, DstWdt, RBY /* Integer */, A, H, E /* Double */, PSrc /* TPoint*/) {
        var PPersp = {}; /* TPoint */

        PPersp.X = Math.round(DstWdt / 2 + A * (PSrc.X - SrcWdt/2) / (H * PSrc.Y + 1));
        //PPersp.Y = Math.round((RBY +  E * PSrc.Y) / (H * PSrc.Y + 1));
        PPersp.Y = DstHgt - Math.round((RBY +  E * PSrc.Y) / (H * PSrc.Y + 1));
        
        return PPersp;
}

var Wd, Hg, YShift /* Integer */, A, H, E /* Double */, Pts = []; /* array[0..3] of TPoint */;

//XPersp = XPCenter + A * (X - XCenter) / (H * Y + 1)
//YPersp = (YShift +  E * Y) / (H * Y + 1)
Wd = 600; // width of source
Hg = 200; // height of source
YShift = Hg / 4;

var { A, H, E } = CalcAxialSymPersp(Wd, Hg, Wd, Wd * 9 / 10, YShift, Wd * 8 / 10, Hg * 3 / 4);

Pts.push( PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: Wd, Y: 0 }) );
Pts.push( PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: Wd, Y: Hg }) );
Pts.push( PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: 0, Y: Hg }) );
Pts.push( PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: 0, Y: 0 }) );

//map and draw central point
var center = PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: Wd / 2, Y: Hg - (Hg / 2) });
document.querySelector('#center').setAttribute("cx", center.X);
document.querySelector('#center').setAttribute("cy", center.Y);

//map and draw point at (0.2,0.2)
var custom_coord = PerspMap(Wd, Hg, Wd, YShift, A, H, E, { X: Wd * 2 / 10, Y: Hg - (Hg * 2 / 10) });
document.querySelector('#custom-coord').setAttribute("cx", custom_coord.X);
document.querySelector('#custom-coord').setAttribute("cy", custom_coord.Y);

// draw a trapeze
document.querySelector('#trapeze').setAttribute("d", `
        M ${Pts[0].X} ${Pts[0].Y}
        L ${Pts[1].X} ${Pts[1].Y}
        L ${Pts[2].X} ${Pts[2].Y}
        L ${Pts[3].X} ${Pts[3].Y}
        Z
`);

/* helper */ document.querySelector('#NW-SE').setAttribute('d', `M${Pts[0].X} ${Pts[0].Y} L${Pts[2].X} ${Pts[2].Y}`);
/* helper */ document.querySelector('#SW-NE').setAttribute('d', `M${Pts[3].X} ${Pts[3].Y} L${Pts[1].X} ${Pts[1].Y}`);
body {
        margin:2px;
}

svg {
        width:600px;
        height:200px;
        background:#ccc;
        border:solid 1px #000;
}

path {
        stroke:#000;
        fill:none;
}
<div id="wrap">
        <svg>
                <path id="trapeze"></path>
                <circle id="center" r="3"></circle>
                <circle fill="red" id="custom-coord" r="3"></circle>
                <path id="NW-SE"></path>
                <path id="SW-NE"></path>
        </svg>
</div>

EDIT #2

I updated demo snippet, it seems to work in some way, the only problem currently i have is that if i specify

Wd = 600; // width of source
Hg = 200; // height of source

the actuall trapeze is smaller (has less width and height),

also in some weird way manipulating this line:

YShift = Hg / 4;

changes the actuall height of trapeze.

its just then difficult to implement, as if i have been given svg court with certain size i need to be able to provide the actuall size to the function so then coords calculations will be accurate.

Lets say that i will be given court where i know 4 corners and based on that i need to be able to calculate coords. This implementation from my demo snippet, doesnt o it unfortunately.

Anyone could help alter the code or provide better implementation?



from convert regular 2d rectangle coords to trapeze

No comments:

Post a Comment