math — преобразовать мировое преобразование в локальное преобразование

У меня немного болит голова… Я использую jsbox2d.js для 2D-игры, основанной на графике SVG. Я хотел бы «подключить» b2d-тело к элементу svg, что уже работает для отладки, но не для графики в сцене.

настраивать:

  • есть SVG, включая произвольную глубоко вложенную структуру групп и фигур. У каждого из них есть некоторые преобразования, поэтому каждый элемент находится в своем собственном координатном пространстве.

  • в стороне есть симуляция box2d, и некоторые тела представляют собой элемент в svg.

Я хотел бы применить преобразование b2body к элементу svg, который он представляет, чтобы анимация выглядела правильно. Он уже работает для отладки, где я использую этот код:

// links is an array like [[b2body, svgelement],…]

for (var i = 0; i < links.length; i++) {
    var t = links[i][0].GetTransform();

    //tiny helper function just taking the values and
    //setting it to the element
    // transform(element, a,b,c,d,e,f)
    //|a c e|
    //|b d f|
    svghelper.transform(links[i][1],
      t.q.c, t.q.s, -t.q.s,
      t.q.c, t.p.x, t.p.y);
}

Основное отличие состоит в том, что элементы, используемые для отладочной отрисовки, генерируются на лету, без каких-либо преобразований, с использованием мировых координат вершин b2dShapes.

Но графика для сцены взята из графики SVG, созданной с помощью inkscape и, т.е. используя группы для руки или головы персонажа.

Как я могу применить преобразование тела прямо к элементам? Я думаю, мне нужно изменить основу матрицы преобразования, но я почему-то не могу заставить ее работать.

Я пробовал это:

var t = body.GetTransform(),
    mtr = svg,createSVGMatrix(
            t.q.c, t.q.s, -t.q.s,
            t.q.c, t.p.x, t.p.y),
    toElement = element.getTransformToElement(element.ownerSVGDocument),
    toElement_inv = toElement.invert();

    mtr = mtr.multiply(toElement);
    mtr = toElement_inv.multiply(mtr);

 //applying the result

Но это привело к неправильному результату и ошибкам при инвертировании матрицы.

Спасибо впереди!


person philipp    schedule 21.03.2014    source источник
comment
getTransformToElement принимает аргумент Element, а не Document. SVGMatrix имеет метод inverse(), а не invert(). Вы пытались просто добавить матрицу преобразования в качестве атрибута? Например, element.setAttribute("transform", "matrix(" + t.q.c + " " + t.q.s + " " + (-t.q.s) + " " + t.q.c + " " + t.p.c + t.p.y + " " + ")");?   -  person Erik Dahlström    schedule 21.03.2014
comment
Возможная проблема: mtr = svg, createSVGMatrix, должно быть mtr = svg.createSVGMatrix, является ли svg идентификатором корневого svg или elem.nearestViewPortElement?   -  person Francis Hemsher    schedule 21.03.2014


Ответы (1)


У меня есть приведенный ниже пример, который может быть полезен и, надеюсь, не добавит вам головной боли;) Он касается размещения элементов в разных видовых портах.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Find X,Y in Transforms &amp; ViewPorts</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Find X,Y in Transforms &amp; ViewPorts</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
Elements are transformed and reside it different viewPorts. This example uses  <b>getScreenCTM</b> and <b>inverse</b> to access x,y values.
</div>
<table>
<tr><td align=left>
<b>Click on an element target.<br />
This will place the black circle<br />so it matches the target's transformations</b><br />
 Scenerio:<br />
1.) SVG image is inline, contained in a DIV.<br />
2.) The blue rect element is contained in a &lt;g&gt;.<br />
3.) The &lt;g&gt; element has been transformed.<br />
4.) The maroon rect resides in a different viewPort.<br />
5.) The orange circle has been transformed.<br />
6.) Move cursor over elements to get their x,y values<br />
6.) Click over element place black circle in its viewport or target's transform.<br />
</td>
<td align=left>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="400" height="400" onmousemove="svgCursor(evt)"  onclick="placeBlackCircle(evt)" >
<circle pointer-events="none" id="blackCircle" r="10" fill="black" />
<circle onmousemove=getXY(evt)  onmouseout=clearXY() id="redCircle" cx="120" cy="180" r="40" fill="red" stroke="black" stroke-width="2" />
<circle onmousemove=getXY(evt) onmouseout=clearXY()  id="orangeCircle" cx="200" cy="200" r="40" fill="orange" stroke="black" stroke-width="2" />
<svg viewBox="0 100 800 800">
<rect onmousemove=getXY(evt) onmouseout=clearXY()  id="maroonRect"  x="220" y="250" width="60" height="60" fill="maroon" stroke="black" stroke-width="2"  />
</svg>
<g id="myG" >
<rect onmousemove=getXY(evt)  onmouseout=clearXY() id="blueRect"  x="220" y="250" width="60" height="60" fill="blue" stroke="black" stroke-width="2"  />
</g>
</svg>
</div>
</td>
<td align=left>
<table style='font-family:lucida console'>
<tr><td colspan=4><b>HTML Page Values:</b></td></tr>
<tr style='font-size:110%'>
<td align=right>mouse X:</td><td><input style='font-size:120%' type=text id=htmlMouseXValue size=1 /></td>
<td><input style='font-size:120%'  type=text id=htmlMouseYValue size=1 /></td><td align=left>:mouse Y</td>
</tr>
<tr><td colspan=4><b>SVG Image Values:</b></td></tr>
<tr style='font-size:110%'>
<td align=right>svg X:</td> <td><input style='font-size:120%'  type=text id=svgXValue size=1 /></td>
<td><input style='font-size:120%'  type=text id=svgYValue size=1 /></td><td align=left>:svg Y</td>
</tr>
<tr><td colspan=4><b>Target:<input id=elemIdValue size=10 /></b></td></tr>
<tr style='font-size:110%'>
<td align=right>client X:</td> <td><input style='font-size:120%'  type=text id=clientXValue size=1 /></td>
<td><input style='font-size:120%'  type=text id=clientYValue size=1 /></td><td align=left>:client Y</td>
</tr>
<tr style='font-size:110%'>
<td align=right>screen X:</td>   <td><input style='font-size:120%'  type=text id=screenXValue size=1 /></td>
<td><input style='font-size:120%'  type=text id=screenYValue size=1 /></td><td align=left>:screen Y</td>
</tr>
</table>
</td>
</tr></table>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<script id=myScript>
//---mouse move---
function getXY(evt)
{
    var target=evt.target
    elemIdValue.value=target.id

    var pnt = target.ownerSVGElement.createSVGPoint();
    pnt.x = evt.clientX;
    pnt.y = evt.clientY;
    clientXValue.value=pnt.x
    clientYValue.value=pnt.y

    //---element's x,y  screen transformed/inversevalues---
    var sCTM = target.getScreenCTM();
    var PNT = pnt.matrixTransform(sCTM.inverse());

    screenXValue.value=PNT.x
    screenYValue.value=PNT.y
}
function placeBlackCircle(evt)
{
    var target=evt.target;
    //---initialize a point in its respective viewport--
    if(target.nearestViewportElement) //--must click on an element not svg root--
    {
        var pnt = target.nearestViewportElement.createSVGPoint();
        //---client area click point---
        pnt.x = evt.clientX;
        pnt.y = evt.clientY;

        var sCTM = target.getScreenCTM();
        //---return viewport's Pnt.x, Pnt.y---
        PNT = pnt.matrixTransform(sCTM.inverse());
        //----place blackDot on top in viewport and locate it---
        target.nearestViewportElement.appendChild(blackCircle)
        blackCircle.setAttribute("cx",PNT.x)
        blackCircle.setAttribute("cy",PNT.y)

        if(target.parentNode.nodeName=="g")
        {
            target.parentNode.appendChild(blackCircle)
        }
        if(target.getAttribute("transform"))
        {
            var transform=target.getAttribute("transform")
            blackCircle.setAttribute("transform",transform)
        }
        else
            blackCircle.removeAttribute("transform")
    }
}

function clearXY()
{
    elemIdValue.value=""
    clientXValue.value=""
    clientYValue.value=""

    screenXValue.value=""
    screenYValue.value=""
}

//---onload---
function initTransforms()
{
//---place some transforms on the elements---

    //--- transform orange circle---
    var transformRequestObj=mySVG.createSVGTransform()
    var animTransformList=orangeCircle.transform
    var transformList=animTransformList.baseVal
    //---translate---
    transformRequestObj.setTranslate(180,-260)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----scale---
    transformRequestObj.setScale(.5,.9)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewY---
    transformRequestObj.setSkewY(52)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()

    //--init Transform on myG---
    var transformRequestObj=mySVG.createSVGTransform()
    var animTransformList=myG.transform
    var transformList=animTransformList.baseVal
    //---translate---
    transformRequestObj.setTranslate(-50,-80)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewX---
    transformRequestObj.setSkewX(15)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //----skewY---
    transformRequestObj.setSkewY(20)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
    //---rotate---
    transformRequestObj.setRotate(30,200,200)
    transformList.appendItem(transformRequestObj)
    transformList.consolidate()
}

document.onmousemove = htmCursor
//---'event' is the html event object---
function htmCursor(event)
{
    var event = event || window.event;
    myMouseX=event.clientX;
    myMouseY=event.clientY;
    myMouseX = myMouseX + document.documentElement.scrollLeft;
    myMouseY = myMouseY + document.documentElement.scrollTop;

    htmlMouseXValue.value=myMouseX
    htmlMouseYValue.value=myMouseY
}
//---'evt' is the svg event object--
function svgCursor(evt)
{
    var rect = svgDiv.getBoundingClientRect();
    svgXValue.value=evt.clientX-rect.left
    svgYValue.value=evt.clientY-rect.top
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
    initTransforms()
    svgSourceValue.value=svgDiv.innerHTML
    jsValue.value=myScript.text
}
</script>

</body>

</html>
person Francis Hemsher    schedule 21.03.2014