Abstract form

CSSMatrix 3D Transformations

Posted in Coding, JavaScript by Homam Hosseini on September 27, 2011

Well I’ve spent a good weekend figuring out CSS3 3D transformations. In short, now I think it is intelligently designed to fit web design needs, however coming from Direct3D background I was looking for a camera in W3C API.

In this post I show how to make a touch sensitive, interactive 3D model of the iPad using matrix transformations.

Although iPad is not a perfect cuboid, for simplicity we assume it is. Google and download pictures of the 6 sides of the iPad. Read this excellent Introduction to CSS 3-D Transforms on how to make 3D cuboids using CSS3. It is fairly easy and straight forward.

Untransformed layers

iPad layers before transformation; six sides stacked on top of each others

I want to rotate the model by touching the screen / moving the mouse. We’re not digging into the details of touch event handling here, check out Touching and Gesturing  on the iPhone for a nice discussion on this subject.

Obviously we move our fingers on the phones screen, or the mouse in 2D flat surfaces, but we want to rotate our iPad in 3D space. Luckily any rotation in 3D space can be decomposed to 3 elemental rotations around the axes of a coordinate system (frame of reference), using Euler angles; meaning that a user can rotate the model to a desired state by no more than 3 gestures.

Transformed iPad ModelCheck out the first draft.

It works; you can freely rotate the model in any direction. But something’s not quite right: the UI response to gestures is not intuitive. Sometimes when you move your fingers to the left, the model rotates upward, another time it rotates down-right…. The problem is that every time that you rotate the model you change the orientation of its axes. You can leave your program as it is, it is sellable and in fact I’ve bought programs with this bug before. The rest of this post explains a solution to this problem.

Linear Transformations

All CSS3 transformations (rotate, scale, skew) are reversible, you can rotate an object 40 degrees clockwise, scale it to 2x bigger, then shrink it to half and rotate it 40 degrees counter clockwise and you end up with the object in its initial state. Another interesting feature of CSS3 transformations is that although an image can be distorted by a transformation, straight lines don’t curve or bend and remain straight under any transformation. Each point of the original image is always mapped to one and only one point of the transformed image. These are the characteristics of linear maps.

Any linear map can be represented by a transformation matrix. In our case, in a 3-dimensional space, it is a 3×3 matrix. I won’t dig into the technical details of matrices, simply because we don’t need to know those details. Check out your old analytic geometry textbook.

Given a transformation matrix M, any point P of our object will be transformed by this matrix product:

P’ = M * P

(Here we use the fact that points can be represented by their position vectors, hence column matrices)

There’s a little thing about translation transformation. In good old geometry, a translation can be represented by a vector (when you translate an object, you move it along a path that has a direction and length), and you find the translated coordinates of a point P by summing up its original coordinates with the translation vector: P’ = P + T.  There’s a trick to combine translation with other forms of linear transformations using 4×4 matrices.

If M is a 4×4 transformation matrix and P is a 4×1 column vector representing the coordinate of a point in space (the last row of the vector is set to 0), then:

P’ = M * P

P’ (the matrix product of M and P) is the coordinate of our point after the object has been transformed by M.

M can represent any state of escalation, rotation, translation, or skewness.

CSSMatrix

CSS gives us the option to define our desired transformation by a 4×4 transformation matrix. This method is virtually useless in CSS declarative way, for a 3D transformation you have to calculate the matrix elements and pass 16 parameters to matrix3d() property function (for an example to see how obscure the code might become, check out rotate3d() definition in W3C’s CSS 3d transforms draft). But it is easy and very convenient to use this matrix in JavaScript code, thanks to DOM’s CSSMatrix interface. Currently (Sep. 2011) WebKit implements this interface by WebKitCSSMatrix type.

We initialize an instance of a WebKitCSSMatrix by passing a correct string value of -webkit-transform CSS property. So one can construct it by something like this:

new WebKitCSSMatrix("scale3d(1,2,1)")
or
new WebKitCSSMatrix("scale3d(1,2,1) rotate3d(0,0,1, 45deg) translate3d(100px, 0, -20px)")

Here’s where this window object’s little useful function comes handy: ‘window.getComputedStyle()‘. getComputedStyle() takes a DOM Element and returns an instance of CSSStyleDecleration that is a representation of all the style properties currently set for the element. It is also a dictionary. You can get the current transform value by: window.getComputedStyle(element)["-webkit-transform"] or by window.getComputedStyle(element).webkitTransform property. Its value is in form of matrix() or matrix3d(). To get the current CSSMatrix that is applied to an element use:

m = new WebKitCSSMatrix(window.getComputedStyle(element).webkitTransform)

CSSMatrix is indeed a 4×4 matrix (its properties are named m11 to m44), its toString() method returns its CSS representation (in matrix() or matrix3d() form).

It also provides a handful of useful functions for matrix manipulation. These functions don’t mutate the object; they return a new instance of CSSMatrix:

  • multiply
  • inverse
  • translate
  • scale
  • rotate
  • rotateAxisAngle
  • skewX
  • skewY

Check out Apple’s documentation.

I learnt it in a hard way that multiply() function doesn’t exactly work as I understand from the documentations. The text says, and I naturally expected that, given matrices A and B, A.multipy(B) must be equal to A * B in math notation. But it turned out that it is actually equal to B * A.

Back to our original problem, let’s differentiate between the model’s frame of reference and the world (device viewport) frame of reference. Your view port (computer’s screen) has a static frame of reference (for our purpose). Viewport axes: Up (Y), Right (X) and Facing you (Z) are attached to the device; they don’t change with respect to the device. But the directions of your 3D model’s axes (X’,Y’,Z’) change as you rotate it inside the viewport. Our UI inconsistent response problem happened because when we move our fingers upward on the device, we expect the model to rotate around device X axis, but rotate3d(1,0,0,#deg) actually rotates the model around its own X’ axis.

Viewport vs Model frame of reference

Luckily rotate3d(z,y,z,#deg) function can rotate the model around any arbitrary axis (defined by vector [x,y,z] here). So the problem boils down to finding, which axis of the rotated object is parallel to the device X and Y axes, after an arbitrary rotation.

We know that rotation can be represented by a linear transformation matrix. If V’ is an arbitrary axis on the model, that was parallel to V (an axis in device frame of reference) prior to the rotation, then we can find what axis on object is now parallel to V after the rotation, by:

V’’ = M * V’

(where M is the transformation matrix that defines the rotation)

If the model is not transformed (when window.getComputedStyle(element).webkitTransform is the identity matrix) V’’ is parallel to V’ parallel to V.

Note that we represent an axis by a vector parallel to it, so the column vector:  represents X axis, [0,1,0] represents Y and [0,0,1] represent Z. A rotation linear transformation can be represented by a 3×3 matrix
M =Transformation matrix (M)

There you go. You can extract CSSMatrix m11..m33 elements and write a little bit of JavaScript to produce V’’. Or use CSSMatrix.multiply() function that takes a CSSMatrix as its argument; then you have to construct a 4×4 representation of axes by just padding the column vector and setting all the other elements to 0.

In JavaScript:

var deviceXAxis = new WebKitCSSMatrix("matrix3d(1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0)");
var deviceYAxis = new WebKitCSSMatrix("matrix3d(0,0,0,0, 1,0,0,0, 0,0,0,0, 0,0,0,0)");

The result of deviceXAxis.multiply(transformation) is the object’s X’’ axis that is currently parallel to device X axis.

The following function rotates the model around device X and Y axes (resulting in a natural user experience):

function rotateModel (xRot, yRot) {
    // get the current transformation matrix:
    var m = new WebKitCSSMatrix(window.getComputedStyle(cube).webkitTransform);
    // Model Y’ axis that is now parallel to device Y axis:
    var yAxis = ipad.deviceYAxis.multiply(m);
    // Rotate around Y’:
    var m1 = m.rotateAxisAngle(yAxis.m11, yAxis.m21, yAxis.m31, yRot);
    // Model X’ axis that is now parallel to device X axis:
    var xAxis = ipad.deviceXAxis.multiply(m1);
    // Rotate around X’:
    var m2 = m1.rotateAxisAngle(xAxis.m11, xAxis.m21, xAxis.m31, xRot);
    // Apply the final rotation matrix to the model:
    cube.style.webkitTransform = m2.toString();
}

Check out the final product here.

Tagged with: , , ,

Canvas Intellisense in Visual Studio

Posted in Coding, Internet, JavaScript by Homam Hosseini on February 18, 2010

I was playing with HTML5 Canvas element to see how it could be useful in future web based game developments. I like that it is easier than GDI. I haven’t yet done much performance testing but it is definitely faster than making games by animating DOM elements.

Recently I had some free time so I decided to create vsdoc documention for Canvas element interface for Visual Studio. I added intellisense (auto competition) and some helps and tips.

Download canvas-vsdoc.js and canvas-utils.js from CodePlex.

It is tuned to work with VS2010, but we can make it work with VS2008 too.

canvas-vsdoc.js contains the intellisense documentation.

canvas-utils.js has a few utility functions (like detecting if the browser supports Canvas) and some enumeration types for things like Line Joins, Repeations, Text Aligns, etc.

To use the intellisense you need to reference canvas-vsdoc.js in the beginning of your JavaScript file, like this:

/// <reference path=”canvas-vsdoc.js” />

Note you can just drop the .js file and Visual Studio will write the reference.

Then use a utility method to get a reference to canvas element:

var canvas = Canvas.vsGet(document.getElementById("canvas1"));

Canvas.vsGet(element) receives a HTML element and returns the given element itself if it is in runtime. But in design time it returns Canvas.vsDoc.VSDocCanvasElement object that contains the documentations.

Then you can use the canvas element as usual:

var ctx = canvas.getContext("2d");
ctx.arc(50, 50, 25, 0, Math.PI, true);
…

Please note canvas-vsdoc.js must not be included  in runtime but canvas-utils.js should be included (if you want to use Canvas.vsGet() and other utilities).

In VS2008 you should trick the environment by assigning the variable that refers the 2D context to Canvas.vsDoc.Canvas2dContext, by something like this:

var ctx = canvas.getContext("2d");
if (typeof DESIGN_TIME != "undefined" && DESIGN_TIME)
ctx = Canvas.vsDoc.Canvas2dContext;

DESIGN_TIME global variable is defined inside canvas-vsdoc.js. In runtime it should be undefined or false.

Just a note: if you still want to work in IE, you will find this Google extension very interesting: http://code.google.com/chrome/chromeframe/

Update: Visual Studio 11 natively supports canvas intellisense.

 

 

 

 

 

 

Hollywood Principle

Posted in Coding, Development, Internet, JavaScript by Homam Hosseini on December 7, 2009

Today I was working on refactoring some names in Hyzobox In/Out API. It is my personal favorite piece of code in the whole platform. In summary it allows the publisher of the game to customize the game in runtime dynamically by injecting codes and executing some functions in the context of the game (In) and to get notified about the events that are occurring inside the game (Out).

Inversion of Control is very natural in JavaScript and it has been used extensively in In/Out API. It’s quite different from popular prototype pattern but most everyday JavaScript programmers use IoC even though they don’t usually notice it.

Here is a sample in our API:

We register an event listener in Hyzobox, waiting for landing view of the game to be loaded:

var hb = Hyzobox.createInstance();
var landingview_loaded = function(hbEvent) {
   // do something with hbEvent
}
hb.addEventListener('landingview_loaded', landingview_loaded);

It’s is clear that we don’t have control over when landingview_loaded event will be fired and its handler will be called.

hbEvent argument that is of type Hyzobox.Event has a data attribute that is of type Object. In this example the data is a LandingView instance. We can interact with this object in the event handler:

var landingview_loaded = function(hbEvent) {
   var view = hbEvent.data;
   view.inject('a-container-id', 'some text');
};

In this example we are injecting ‘some text’ to an element identified by ‘a-container-id’ inside landing view.

addEventListener() is part of Out and inject() is part of In API. If we want to listen to the events that are occurring inside a particular view, we should register their halnders after the view has been loaded:

var landingview_loaded = function(hbEvent) {
   var view = hbEvent.data;
   view.attachEventListener('playNow', function playNowHandler(playNowHbEvent) {
       // do something with playNowHbEvent
   });
};

And so on, we can have many nested Ins and Outs.

It’s easy to see that in these examples the control has been transferred to the event handlers. The caller raise the events but it’s the responsibility of the handlers to control the functionalities.

I don’t want to go into the the details now, because the work hasn’t yet been finished on these APIs. Once released, we will post them in Hyzobox documentations and Hyzonia tech blog in the following weeks.

Tagged with: , , ,

Accessing Remote ASP.NET Web Services using JSONP

Posted in Coding, Development, JavaScript by Homam Hosseini on October 12, 2009

The problem:

You cannot call remote ASP.NET web service methods from a JavaScript, AJAX client.

Example:

You have a web service, at this address: http://a.com/service.asmx and you’ve configured the service to work with AJAX clients:

[WebService
(Namespace = "http://www.hyzonia.com/gametypes/PopNDropLikeGame/WS2")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class GameService : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public GameSessionResponse CreateGameSession(Guid questId)
    {
...
    }
}
}

And it works fine when you call its methods from a web page that is in this address: http://a.com/page.htm:

$.ajax({
        type: "POST",
        url: "GameService.asmx/CreateGameSession",
        data: "{questId: '" + questId + "'}",
        cache: false,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(response) {
            Game._onSessionGot(response.d);
        }
    });

But the very same client-side code doesn’t work from this address: http://b.clom/page.htm

The problem in depth:

At first I cannot hold myself and not say that it is a silly problem. web services are meant to be called by remote clients. The fact that browsers block access to web services by AJAX calls is clearly contrary to the purpose of web services.

Interestingly browser extensions like Flash and Silverlight also by default block remote web services, but they provide a work around. Unfortunately no browser by date supports this work around for XMLHTTPRequests. This “security measure” seems odder when we notice that it is perfectly correct to import a JavaScript code snippet from another domain by a script tag:

<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"
type="text/javascript"></script>

 

The solution:

As it was said, Flash and Silverlight both support remote calls. You just need a clientaccesspolicy file to be hosted at the root of a.com (http://a.com/clientaccesspolicy.xml):

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="SOAPAction">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

This file allows remote calls to be made from any other domain.

But in many situations we want to call the web service methods directly by AJAX clients. This need was the cause of to the development of JSONP (JSON with padding) protocol. As it was discussed it is correct to have a <script> element that loads a script from another domain. On the other hand you may know that it is possible to load scripts dynamically by a simple JavaScript trick (writing<script> tags) or using this jQuery plug in. Now the bulbs are flickering! The solution is to access the JSON web service by the src attribute of a <script> element. This is the whole idea behind JSONP.

But there are a couple of problems needed to be solved for ASP.NET ASMX web services before we can use them in a JSONP scenario.

  1. ASP.NET web services by default only accept POST requests, a <script src=””> element, produces a GET request.
  2. The result of the web method call must conform to JSONP, and you guess, ASP.NET 3.5 by default doesn’t support it.

The solution to the first problem may seem trivial, we can easily enable GET calls to web methods using [ScriptMethod(UseHttpGet = true)] attribute. The immediate problem is that when we mark a web method by this attribute it only can be called by GET requests. And remember, other clients (actually anything other than JSONP clients) are supposed to communicate with the web service by POST requests. I usually end up inheriting from the original web service and marking web methods by [ScriptMethod(UseHttpGet = true)] attribute in the derived class. Therefore I will have two ASMX web services, one using the original class (expecting POST request) and the other using the derived class (expecting GET requests).

[WebMethod(), ScriptMethod(UseHttpGet = true)]
public override GameSessionResponse CreateGameSession(Guid questId)
{
   return base.CreateGameSession(questId);
}

Note you may need to add this code snippet in web.config:

<system.web>
 <webServices>
   <protocols>
     <add name="HttpGet"/>
   </protocols>
 </webServices>
…
</system.web>

There’s another problem to be addressed in the client side. The client should call the web method by a correct URL (it has to pass a correct query string that could be deserialized back to .NET objects in the server side). In case of POST requests, I’m used to JSON2 library to post data to ASP.NET ASMX web services. Jquery $.AJAX method (when it is configured to use JSONP, by dataType: “jsonp”) creates query string parameters for the data object it receives. But the result is not usable for ASMX web services.

Luckily there’s a ready to use JQuery plug in (jMsAjax) that has the required algorithms for serializing a JavaScript object into a query string that can be parsed by ASP.NET web services.

Using the plug in I created this function to serialize JavaScript objects into query strings:

$.jmsajaxurl = function(options) {
    var url = options.url;
    url += "/" + options.method;
    if (options.data) {
       var data = ""; for (var i in options.data) {
       if (data != "")
         data += "&"; data += i + "=" + msJSON.stringify(options.data[i]);
       }
       url += "?" + data; data = null; options.data = "{}";
   }
   return url;
};

You will need jMsAjax for this code snippet to work.

Finally this is a sample of a client side code using JQuery that calls an ASMX web service using JSONP:

var url = $.jmsajaxurl({
    url: "http://hiddenobjects.hyzonia.com/services/GameService3.asmx",
    method: "Login",
    data: { email: "myemail@mydomain.com", password: "mypassword" }
});

$.ajax({
    cache: false,
    dataType: "jsonp",
    success: function(d) { console.log(d); },
    url: url + "&format=json"
});

Or equivalently:

$.getJSON(url + "&callback=?&format=json", function(data) {
    console.log(data);
});

When you call an ASP.NET web service method (that is configured to receive GET requests) using a code similar to the above, it returns in XML. The problem is that the web service expects to receive a request that has a content type of “application/json; charset=utf-8” and <script> element simply doesn’t add this content type to the request. There’s a little thing we can do at the client side. The easiest way to resolve this problem is to use a HTTP module. The HTTP module should add this content type to the requests before they are processed by the web service handler.

On the other hand a JSONP client expects that the web service return the call by a string like this:

nameOfACallBackFunction(JSON_OBJECT_WEB_METHOD_RETURNED)

nameOfACallBackFunction must be given to the server by a parameter in the query string. Different JSONP compatible web services use different names for this parameter, but usually it is named ‘callback’. At least this is what $.ajax() automatically adds to the request in JSONP mode.

I grabbed this HTTP module from a post in elegantcode.com.

public class JsonHttpModule : IHttpModule
{
    private const string JSON_CONTENT_TYPE = "application/json; charset=utf-8";

    #region IHttpModule Members
    public void Dispose()
    {
    }

    public void Init(HttpApplication app)
    {
        app.BeginRequest += OnBeginRequest;
        app.ReleaseRequestState += OnReleaseRequestState;
    }
    #endregion

    bool _Apply(HttpRequest request)
    {
        if (!request.Url.AbsolutePath.Contains(".asmx")) return false;
        if ("json" != request.QueryString.Get("format")) return false;
        return true;
    }

    public void OnBeginRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        if (!_Apply(app.Context.Request)) return;

        if (string.IsNullOrEmpty(app.Context.Request.ContentType))
        {
            app.Context.Request.ContentType = JSON_CONTENT_TYPE;
        }
    }

    public void OnReleaseRequestState(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        if (!_Apply(app.Context.Request)) return;

        app.Context.Response.Filter = new JsonResponseFilter(app.Context.Response.Filter, app.Context);
    }
}

public class JsonResponseFilter : Stream
{
    private readonly Stream _responseStream;
    private HttpContext _context;

    public JsonResponseFilter(Stream responseStream, HttpContext context)
    {
        _responseStream = responseStream;
        _context = context;
    }

    public override bool CanRead { get { return true; } }

    public override bool CanSeek { get { return true; } }

    public override bool CanWrite { get { return true; } }

    public override long Length { get { return 0; } }

    public override long Position { get; set; }

    public override void Write(byte[] buffer, int offset, int count)
    {
        var b1 = Encoding.UTF8.GetBytes(_context.Request.Params["callback"] + "(");
        _responseStream.Write(b1, 0, b1.Length);
        _responseStream.Write(buffer, offset, count);
        var b2 = Encoding.UTF8.GetBytes(");");
        _responseStream.Write(b2, 0, b2.Length);
    }

    public override void Close()
    {
        _responseStream.Close();
    }

    public override void Flush()
    {
        _responseStream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _responseStream.Seek(offset, origin);
    }

    public override void SetLength(long length)
    {
        _responseStream.SetLength(length);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _responseStream.Read(buffer, offset, count);
    }
}

This HTTP module will be applied to each request to a .asmx file that has a format=json in its query string.

Note that you have to update web.config:

<system.web>
…
  <httpModules>
    …
    <add name="JSONAsmx"/>
  </httpModules>
</system.web>

For IIS6 and

<system.webServer>
  <modules>
  …
    <add name="JSONAsmx"/>
  </modules>
  …
</system.webServer>

For IIS7.

Now to test is open your web service in your browser, in my example:

http://hiddenobjects.hyzonia.com/services/GameService3.asmx/Login?email=e@e.com&password=p

It should return in XML

And

http://hiddenobjects.hyzonia.com/services/GameService3.asmx/Login?email=”e@e.com”&password=”p”&format=json&callback=myCallBackFunc

Will return:

myCallBackFunc({"d":{"__type":"HLoginResponse",
"isSuccessful":false,"error":false,"authSessionId":null,"nickName":null,"score":0}});

Don’t worry about myCallBackFunc, JQuery nicely manages it, so that the whole business is behind the scene and you can use $.ajax success callback the very same way you use it for a normal AJAX call.

We should note that JSONP has its own problems, especially… yes… in IE! All versions of Internet Explorer has a 2083 character limit for the URL of a request. It means that you cannot send large data in GET requests to the server. Sometime this limitation leaves us with no choice but to use Flash or create a proxy to the remote web service in the local domain.