@sky-foundry/two.js
Version:
A renderer agnostic two-dimensional drawing api for the web.
246 lines (178 loc) • 5.95 kB
JavaScript
(function(Two) {
var Path = Two.Path, PI = Math.PI, TWO_PI = Math.PI * 2, HALF_PI = Math.PI / 2,
cos = Math.cos, sin = Math.sin, abs = Math.abs, _ = Two.Utils;
var ArcSegment = Two.ArcSegment = function(ox, oy, ir, or, sa, ea, res) {
var amount = res || (Two.Resolution * 3);
var points = _.map(_.range(amount), function() {
return new Two.Anchor();
});
Path.call(this, points, true, false, true);
this.innerRadius = ir;
this.outerRadius = or;
this.startAngle = sa;
this.endAngle = ea;
this._update();
this.translation.set(ox, oy);
}
_.extend(ArcSegment, {
Properties: ['startAngle', 'endAngle', 'innerRadius', 'outerRadius'],
MakeObservable: function(obj) {
Path.MakeObservable(obj);
_.each(ArcSegment.Properties, Two.Utils.defineProperty, obj);
}
});
_.extend(ArcSegment.prototype, Path.prototype, {
_flagStartAngle: false,
_flagEndAngle: false,
_flagInnerRadius: false,
_flagOuterRadius: false,
_startAngle: 0,
_endAngle: TWO_PI,
_innerRadius: 0,
_outerRadius: 0,
constructor: ArcSegment,
_update: function() {
if (this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius
|| this._flagOuterRadius) {
var sa = this._startAngle;
var ea = this._endAngle;
var ir = this._innerRadius;
var or = this._outerRadius;
var connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);
var punctured = ir > 0;
var vertices = this.vertices;
var length = (punctured ? vertices.length / 2 : vertices.length);
var command, id = 0;
if (connected) {
length--;
} else if (!punctured) {
length -= 2;
}
/**
* Outer Circle
*/
for (var i = 0, last = length - 1; i < length; i++) {
var pct = i / last;
var v = vertices[id];
var theta = pct * (ea - sa) + sa;
var step = (ea - sa) / length;
var x = or * Math.cos(theta);
var y = or * Math.sin(theta);
switch (i) {
case 0:
command = Two.Commands.move;
break;
default:
command = Two.Commands.curve;
}
v.command = command;
v.x = x;
v.y = y;
v.controls.left.clear();
v.controls.right.clear();
if (v.command === Two.Commands.curve) {
var amp = or * step / Math.PI;
v.controls.left.x = amp * Math.cos(theta - HALF_PI);
v.controls.left.y = amp * Math.sin(theta - HALF_PI);
v.controls.right.x = amp * Math.cos(theta + HALF_PI);
v.controls.right.y = amp * Math.sin(theta + HALF_PI);
if (i === 1) {
v.controls.left.multiplyScalar(2);
}
if (i === last) {
v.controls.right.multiplyScalar(2);
}
}
id++;
}
if (punctured) {
if (connected) {
vertices[id].command = Two.Commands.close;
id++;
} else {
length--;
last = length - 1;
}
/**
* Inner Circle
*/
for (i = 0; i < length; i++) {
pct = i / last;
v = vertices[id];
theta = (1 - pct) * (ea - sa) + sa;
step = (ea - sa) / length;
x = ir * Math.cos(theta);
y = ir * Math.sin(theta);
command = Two.Commands.curve;
if (i <= 0) {
command = connected ? Two.Commands.move : Two.Commands.line;
}
v.command = command;
v.x = x;
v.y = y;
v.controls.left.clear();
v.controls.right.clear();
if (v.command === Two.Commands.curve) {
amp = ir * step / Math.PI;
v.controls.left.x = amp * Math.cos(theta + HALF_PI);
v.controls.left.y = amp * Math.sin(theta + HALF_PI);
v.controls.right.x = amp * Math.cos(theta - HALF_PI);
v.controls.right.y = amp * Math.sin(theta - HALF_PI);
if (i === 1) {
v.controls.left.multiplyScalar(2);
}
if (i === last) {
v.controls.right.multiplyScalar(2);
}
}
id++;
}
// Final Point
vertices[id].copy(vertices[0]);
vertices[id].command = Two.Commands.line;
} else if (!connected) {
vertices[id].command = Two.Commands.line;
vertices[id].x = 0;
vertices[id].y = 0;
id++;
// Final Point
vertices[id].copy(vertices[0]);
vertices[id].command = Two.Commands.line;
}
}
Path.prototype._update.call(this);
return this;
},
flagReset: function() {
Path.prototype.flagReset.call(this);
this._flagStartAngle = this._flagEndAngle
= this._flagInnerRadius = this._flagOuterRadius = false;
return this;
},
clone: function(parent) {
var ir = this.innerRadius;
var or = this.outerradius;
var sa = this.startAngle;
var ea = this.endAngle;
var resolution = this.vertices.length;
var clone = new ArcSegment(0, 0, ir, or, sa, ea, resolution);
clone.translation.copy(this.translation);
clone.rotation = this.rotation;
clone.scale = this.scale;
_.each(Two.Path.Properties, function(k) {
clone[k] = this[k];
}, this);
if (parent) {
parent.add(clone);
}
return clone;
}
});
ArcSegment.MakeObservable(ArcSegment.prototype);
function mod(v, l) {
while (v < 0) {
v += l;
}
return v % l;
}
})((typeof global !== 'undefined' ? global : (this || window)).Two);