What in the Universe could be potentially more fun than using JavaScript to play with 2D graphics …?
Ok, well, suddenly a bunch of things comes to my mind… But still JavaScript is a cool technology, especially when we consider the number of frameworks and libs gravitating around it, so keep on reading!
I wanted to get my hands dirty writing some JavaScript code in order to get a bit more familiar with the language, so I decided to write a little app in the continuity of one of my previous post: Giving a try at HTML5 canvas and Javascript
Those two have nothing in common except the idea of creating an AutoCAD-like UI to create and manipulate 2D entities. This post focused more on the user interaction with existing entities.
After my trial in the mentioned post, I realized that achieving my goal using the bare Canvas API and SVG would be quite challenging and probably outside the scope of a simple blog post!
I recalled that my colleague, the blog-wide famous Jeremy Tammik, used a 2D JavaScript library called Raphaël in one his post: 2D SVG Editing on Mobile Device with Raphaël.
After taking a look at the Raphaël samples on their website and googling around, it appeared to me as a pretty good reference. I was also able to easily find further samples around the dev community, in sites such as stackoverflow.
The implementation
My app would be a graphical window allowing user to select 2D entities and interact with them using an AutoCAD-like style based on grip points. To achieve that I wanted to use an object oriented approach based on a collection of basic classes as follow:
JsCADObject
|--------------> JsCADGripPoint
|--------------> JsCADEntity
|--------------> JsCADLine
|--------------> JsCADCircle
|--------------> JsCADPLine
Pretty much self-explanatory, especially if you are familiar with ObjectARX, the AutoCAD SDK. What is less obvious is the way you can handle classes using JavaScript. So just to make things clear, the code I am presenting here should be considered as an experiment, I would by no mean consider myself as a JavaScript expert and recommend such a design or another. JavaScript is a Prototype-based or Prototypal language, by opposition to Class-based OO languages than C++ or Java developers are more experienced with.
Here is a way to achieve inheritance through JavaScript:
///////////////////////////////////////////////////////////
// JsCadEntity - A base class for graphic entities
//
///////////////////////////////////////////////////////////
function JsCadEntity(paper, element, nbGrips) {
// implementation which we don’t care here ...
}
///////////////////////////////////////////////////////////
// JsCadLine - Line entity
//
///////////////////////////////////////////////////////////
function JsCadLine(paper, startPoint, endPoint) {
// implementation which we don’t care here ...
// calls base class constructor
JsCadEntity.call(this, paper, set, 3);
}
// sets up inheritance
JsCadLine.prototype = Object.create(JsCadEntity.prototype);
// Another way to set up inheritance
//JsCadLine.prototype = new JsCadObject;
//JsCadLine.prototype.constructor = JsCadLine;
Once this was sorted out, carrying with the actual implementation was rather straightforward. I had to rely on a couple of Raphael features, such as drawing paths and circle, handling user drag operations or setting visibility and style of my entities.
The Demo
Without considering the implementation of my JsCADEntities, here is the source code for that demo (complete source code attached at the bottom):
window.onload = function () {
// create paper object 500 x 500
var paper = new Raphael(
document.getElementById('canvas_container'),
500,
500);
// create line from (10, 10) to (250, 250)
var line = new JsCadLine(
paper,
[10.0, 10.0],
[250.0, 250.0]);
// create circle at [350.0, 150.0] with radius = 100
var circle = new JsCadCircle(
paper,
[350.0, 150.0],
100);
// create pline using vertex array
var vertices = [
[10.0, 300.0],
[50.0, 450.0],
[150.0, 300.0],
[200.0, 400.0],
[400.0, 300.0]
];
var pline = new JsCadPLine(
paper,
vertices);
document.onkeydown = onKeyDown;
}
// Holds currently selected entities
var selectSet = [];
function onKeyDown(e) {
e = e || window.event;
console.log("onKeyDown - " + e.keyCode);
// escape
if (e.keyCode == '27') {
// Unselect entities and
// clears selectset upon ESCAPE
for (var i = 0; i < selectSet.length; i++) {
selectSet[i].hideGripPoints();
}
selectSet.length = 0;
}
}
--
Use:
-
. Select an entity by clicking on it to make its grippoints visible
. Grab a grippoint and drag it around to modify the entity
. Pres ESC to unselect entities
-
Super cool indeed!
Posted by: Jeremy Tammik | 02/06/2014 at 04:05 PM