/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.drawing.util.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.drawing.util.common"] = true;
dojo.provide("dojox.drawing.util.common");
dojo.require("dojox.math.round");
(function(){
var uidMap = {};
var start = 0;
dojox.drawing.util.common = {
// summary:
// A collection of common methods used for DojoX Drawing.
// This singleton is accessible in most Drawing classes
// as this.util
//
// NOTE:
// A lot of functions use a EventObject
// as an argument. An attempt was made to accept
// either that object or a list of numbers. That wasn't
// finished (it didn't work well in all cases) but is
// likely to happen in the future.
// In cases where you are not sending a Mouse object,
// form your argument like so:
// var obj = {
// start:{
// x:Number, // start x
// y:Number // start y
// },
// x: Number, // end x
// y:Number // end y
// }
//
//
radToDeg: function(/*Numer*/n){
// summary:
// Convert the passed number to degrees.
return (n*180)/Math.PI; // Number
},
degToRad: function(/*Numer*/n){
// summary:
// Convert the passed number to radians.
return (n*Math.PI)/180; // Number
},
angle: function(/*EventObject*/obj, /* ? Float */snap){
// summary:
// Return angle based on mouse object
// arguments:
// obj: EventObject
// Manager.Mouse event.
// snap: Float
// Returns nearest angle within snap limits
//
//obj = this.argsToObj.apply(this, arguments);
if(snap){
snap = snap/180;
var radians = this.radians(obj),
seg = Math.PI * snap,
rnd = dojox.math.round(radians/seg),
new_radian = rnd*seg;
return dojox.math.round(this.radToDeg(new_radian)); // Whole Number
}else{
return this.radToDeg(this.radians(obj)); // Float
}
},
oppAngle: function(/*Angle*/ang){
(ang+=180) > 360 ? ang = ang - 360 : ang;
return ang;
},
radians: function(/*EventObject*/o){
// summary:
// Return the radians derived from the coordinates
// in the Mouse object.
//
//var o = this.argsToObj.apply(this, arguments);
return Math.atan2(o.start.y-o.y,o.x-o.start.x);
},
length: function(/*EventObject*/o){
// summary:
// Return the length derived from the coordinates
// in the Mouse object.
//
return Math.sqrt(Math.pow(o.start.x-o.x, 2)+Math.pow(o.start.y-o.y, 2));
},
lineSub: function(/*Number*/x1, /*Number*/y1, /*Number*/x2, /*Number*/y2, /*Number*/amt){
// summary:
// Subtract an amount from a line
// description:
// x1,y1,x2,y2 represents the Line. 'amt' represents the amount
// to subtract from it.
//
var len = this.distance(this.argsToObj.apply(this, arguments));
len = len < amt ? amt : len;
var pc = (len-amt)/len;
var x = x1 - (x1-x2) * pc;
var y = y1 - (y1-y2) * pc;
return {x:x, y:y}; // Object
},
argsToObj: function(){
// summary:
// Attempts to determine in a Mouse Object
// was passed or indiviual numbers. Returns
// an object.
//
var a = arguments;
if(a.length < 4){ return a[0]; }
return {
start:{
x:a[0],
y:a[1]
},
x:a[2],
y:a[3]//,
//snap:a[4]
}; // Object
},
distance: function(/*EventObject or x1,y1,x2,y2*/){
// summary:
// Return the length derived from the coordinates
// in the Mouse object. Different from util.length
// in that this always returns an absolute value.
//
var o = this.argsToObj.apply(this, arguments);
return Math.abs(Math.sqrt(Math.pow(o.start.x-o.x, 2)+Math.pow(o.start.y-o.y, 2))); // Number
},
slope:function(/*Object*/p1, /*Object*/p2){
// summary:
// Given two poits of a line, returns the slope.
if(!(p1.x-p2.x)){ return 0; }
return ((p1.y-p2.y)/(p1.x-p2.x)); // Number
},
pointOnCircle: function(/*Number*/cx, /*Number*/cy, /*Number*/radius, /*Number*/angle){
// summary:
// A *very* helpful method. If you know the center
// (or starting) point, length and angle, find the
// x,y point at the end of that line.
//
var radians = angle * Math.PI / 180.0;
var x = radius * Math.cos(radians);
var y = radius * Math.sin(radians);
return {
x:cx+x,
y:cy-y
}; // Object
},
constrainAngle: function(/*EventObject*/obj, /*Number*/min, /*Number*/max){
// summary:
// Ensures the angle in the Mouse Object is within the
// min and max limits. If not one of those limits is used.
// Returns an x,y point for the angle used.
//
var angle = this.angle(obj);
if(angle >= min && angle <= max){
return obj; // Object
}
var radius = this.length(obj);
var new_angle = angle > max ? max : min - angle < 100 ? min : max;
return this.pointOnCircle(obj.start.x,obj.start.y,radius, new_angle); // Object
},
snapAngle: function(/*EventObject*/obj, /*Float*/ca){
// summary:
// Snaps a line to the nearest angle
// obj: Mouse object (see dojox.drawing.Mouse)
// ca: Fractional amount to snap to
// A decimal number fraction of a half circle
// .5 would snap to 90 degrees
// .25 would snap to 45 degrees
// .125 would snap to 22.5 degrees, etc.
//
var radians = this.radians(obj),
radius = this.length(obj),
seg = Math.PI * ca,
rnd = Math.round(radians/seg),
new_radian = rnd*seg,
new_angle = this.radToDeg(new_radian),
pt = this.pointOnCircle(obj.start.x,obj.start.y,radius,new_angle);
return pt; // Object
},
// helpers
idSetStart: function(num){
start=num;
},
uid: function(/* ? String */str){
// summary:
// Creates a unique ID.
// arguments:
// str: String
// If provided, kept in a map, incremented
// and used in the id. Otherwise 'shape' is used.
//
str = str || "shape";
uidMap[str] = uidMap[str]===undefined ? start : uidMap[str] + 1;
return str + uidMap[str]; // String
},
abbr: function(type){
// summary:
// Converts a namespace (typically a tool or a stencil) into
// an abbreviation
return type.substring(type.lastIndexOf(".")+1).charAt(0).toLowerCase()
+ type.substring(type.lastIndexOf(".")+2);
},
mixin: function(o1, o2){
// TODO: make faster
//return dojo.mixin(dojo.clone(o1), dojo.clone(o2));
},
objects:{}, //private?
register: function(/*Object*/obj){
// summary:
// Since util is the only Singleton in Drawing (besides
// keys) it is used to help connect the Drawing object
// the Toolbar. Since multiple drawings can be on one
// page, this function serves a little more use than
// on first apearance.
this.objects[obj.id] = obj;
},
byId: function(/*String*/id){
// summary:
// Get an object that was registered with util.register
//
return this.objects[id];
},
attr: function(/* Object */ elem, /* property */ prop, /* ? value */ value, squelchErrors){
// summary:
// Helper function to attach attributes to SVG and VML raw nodes.
//
if(!elem){ return false; }
try{
// util is a crappy check, but we need to tell the diff
// between a Drawing shape and a GFX shape
if(elem.shape && elem.util){
elem = elem.shape;
}
if(!value && prop=="id" && elem.target){
var n = elem.target;
while(!dojo.attr(n, "id")){
n = n.parentNode;
}
return dojo.attr(n, "id");
}
if(elem.rawNode || elem.target){
var args = Array.prototype.slice.call(arguments);
args[0] = elem.rawNode || elem.target;
return dojo.attr.apply(dojo, args);
}
return dojo.attr(elem, "id");
}catch(e){
if(!squelchErrors){
// For debugging only. These errors actually cause errors in IE's console
//console.error("BAD ATTR: prop:", prop, "el:", elem)
//console.error(e)
//console.trace();
}
return false;
}
}
};
})();
}