Catenary
参考
http://www.mathhelpforum.com/math-help/f6/catenary-cable-different-heights-96398.html
/**
* Copyright poiasd ( http://wonderfl.net/user/poiasd )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/8Bnl
*/
package {
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
[SWF (width = "465", height = "465", frameRate = "30", backgroundColor = "0xFFFFFF")]
public class Main extends Sprite {
private const _EPSILON:Number = 1e-6;
private const _LENGTH:Number = 400;
private const _SEGMENTS:uint = 20;
private var _stageWidth:int;
private var _stageHeight:int;
private var _canvas:Shape;
private var _target:Handle = null;
private var _offset:Point = new Point();
private var _handle1:Handle;
private var _handle2:Handle;
private var _distance:Number;
public function Main() {
_init();
}
private function _init():void {
stage.scaleMode = "noScale";
stage.align = "TL";
_stageWidth = stage.stageWidth;
_stageHeight = stage.stageHeight;
graphics.beginFill(0x111111);
graphics.drawRect(0, 0, _stageWidth, _stageHeight);
graphics.endFill();
_canvas = addChild(new Shape()) as Shape;
_handle1 = addChild(new Handle((_stageWidth >> 1) - 125, 100)) as Handle;
_handle2 = addChild(new Handle((_stageWidth >> 1) + 125, 100)) as Handle;
_handle1.target = _handle2;
_handle2.target = _handle1;
_distance = Point.distance(_handle1.position, _handle2.position);
stage.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUpHandler);
_update();
}
private function _mouseDownHandler(event:MouseEvent):void {
if (event.target.name == "handle") {
_target = event.target as Handle;
_offset.x = _target.x - stage.mouseX;
_offset.y = _target.y - stage.mouseY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, _mouseMoveHandler);
}
}
private function _mouseMoveHandler(event:MouseEvent):void {
var h1:Handle = _target;
var h2:Handle = _target.target;
h1.x = stage.mouseX + _offset.x;
h1.y = stage.mouseY + _offset.y;
var dx:Number = h2.x - h1.x;
var dy:Number = h2.y - h1.y;
_distance = Math.sqrt(dx * dx + dy * dy);
if (_distance > _LENGTH) {
h1.x = h2.x - _LENGTH * dx / _distance;
h1.y = h2.y - _LENGTH * dy / _distance;
_distance = _LENGTH;
}
_update();
event.updateAfterEvent();
}
private function _mouseUpHandler(event:MouseEvent):void {
if (_target) {
_target = null;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, _mouseMoveHandler);
}
}
private function _update():void {
var p1:Point = _handle1.position;
var p2:Point = _handle2.position;
if (p1.x > p2.x) {
var temp:Point = p1;
p1 = p2;
p2 = temp;
}
var g:Graphics = _canvas.graphics;
g.clear();
g.lineStyle(1, 0xFFFFFF, 0.8);
if (_distance < _LENGTH) {
if (p2.x - p1.x > 0.01) {
var d:Number = p2.x - p1.x;
var h:Number = p2.y - p1.y;
var a:Number = -_getCatenaryParameter(d, h, _LENGTH);
var x:Number = (a * Math.log((_LENGTH + h) / (_LENGTH - h)) - d) * 0.5;
var y:Number = a * _cosh(x / a);
var offsetX:Number = p1.x - x;
var offsetY:Number = p1.y - y;
_drawCatenary(a, p1, p2, offsetX, offsetY);
} else {
var mx:Number = (p1.x + p2.x) * 0.5;
var my:Number = (p1.y + p2.y + _LENGTH) * 0.5;
g.moveTo(p1.x, p1.y);
g.lineTo(mx, my);
g.lineTo(p2.x, p2.y);
}
} else {
g.moveTo(p1.x, p1.y);
g.lineTo(p2.x, p2.y);
}
}
private function _getCatenaryParameter(d:Number, h:Number, length:Number):Number {
var m:Number = Math.sqrt(length * length - h * h) / d;
var x:Number = _acosh(m) + 1;
var prevx:Number = -1;
var count:int = 0;
while (Math.abs(x - prevx) > _EPSILON && count < 100) {
prevx = x;
x = x - (_sinh(x) - m * x) / (_cosh(x) - m);
count++;
}
return d / (2 * x);
}
private function _drawCatenary(a:Number, p1:Point, p2:Point, offsetX:Number, offsetY:Number):void {
var data:Array = [p1.x, a * _cosh((p1.x - offsetX) / a) + offsetY];
var d:Number = p2.x - p1.x;
var length:int = _SEGMENTS - 1;
for (var i:int = 0; i < length; i++) {
var x:Number = p1.x + d * (i + 0.5) / length;
var y:Number = a * _cosh((x - offsetX) / a) + offsetY;
data.push(x, y);
}
data.push(p2.x, a * _cosh((p2.x - offsetX) / a) + offsetY);
SmoothCurve.draw(_canvas.graphics, data);
}
private function _sinh(x:Number):Number {
return (Math.exp(x) - Math.exp(-x)) * 0.5;
}
private function _cosh(x:Number):Number {
return (Math.exp(x) + Math.exp(-x)) * 0.5;
}
private function _acosh(x:Number):Number {
return Math.log(x + Math.sqrt(x * x - 1));
}
}
}
import flash.display.Graphics;
import flash.display.Sprite;
import flash.geom.Point;
class Handle extends Sprite {
public var target:Handle;
public function Handle(x:Number = 0, y:Number = 0) {
graphics.beginFill(0xF69824, 0.3);
graphics.drawCircle(0, 0, 6);
graphics.beginFill(0xF6C824, 0.8);
graphics.drawCircle(0, 0, 2);
graphics.endFill();
this.x = x;
this.y = y;
name = "handle";
buttonMode = true;
}
public function get position():Point {
return new Point(x, y);
}
}
class SmoothCurve {
public static function draw(canvas:Graphics, data:Array):void {
var length:int = data.length * 0.5 - 1;
var ox:Number = data[2];
var oy:Number = data[3];
canvas.moveTo(data[0], data[1]);
for (var i:int = 2; i < length; i++) {
var x:Number = data[i * 2];
var y:Number = data[i * 2 + 1];
var mx:Number = (x + ox) * 0.5;
var my:Number = (y + oy) * 0.5;
canvas.curveTo(ox, oy, mx, my);
ox = x;
oy = y;
}
length = data.length;
canvas.curveTo(data[length - 4], data[length - 3], data[length - 2], data[length - 1]);
}
}