Как нарисовать синусоидальный линейный график?

Я пытаюсь нарисовать статический график на основе массива чисел. Этот график должен быть красивым, гладким, похожим на синус. Нижние значения всегда должны быть равны нулю, верхние значения указываются в массиве чисел.

образец

Я пытался добиться этого эффекта с помощью curveTo(), но безуспешно.

РЕДАКТИРОВАТЬ: значения такие: 10, 15, 40, 28, 5, 2, 27 и т. д.

Кто-нибудь может помочь, пожалуйста?


person MaRmAR    schedule 28.06.2011    source источник


Ответы (1)


CurveTo() рисует только квадратичные кривые Безье, которые по сути являются сегментами параболы. Следовательно, вам нужно придумать какой-то хитрый алгоритм, если вы хотите нарисовать синус с переменной амплитудой.

Я предлагаю вам нарисовать кривую с прямыми отрезками. Это потребует довольно много итераций в цикле, но это очень простой подход, который можно закодировать всего за несколько минут. Вам действительно нужно что-то более сложное?

Кроме того, посмотрите на http://code.google.com/p/bezier/ : их демка крутая.

Если вы хотите создать что-то, что не может быть легко определено с помощью одного уравнения, вы можете взглянуть на приближение Лагранжа: оно интерполирует заданные точки в гладкую функцию.

EDIT Пример приближения Лагранжа

Код ниже рисует следующее:

введите здесь описание изображения

package
{
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.geom.Rectangle;

    import org.noregret.math.LagrangeApproximator;

    [SWF(fps="31",width="600",height="600",backgroundColor="0xFFFFFF")]    
    public class LagrangeTest extends Sprite
    {
        private const plot:Sprite = new Sprite();
        private const approx:LagrangeApproximator = new LagrangeApproximator();

        public function LagrangeTest()
        {
            super();
            initialize();
        }

        private function initialize():void
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.addEventListener(Event.RESIZE, onResize);
            onResize();

            addChild(plot);

            // ADDING CONTROL POINTS (just took them at random)
            approx.addValue(0, 0);
            approx.addValue(50, -10);
            approx.addValue(100, 10);
            approx.addValue(150, -20);
            approx.addValue(200, -10);
            approx.addValue(250, -20);
            approx.addValue(300, -10);
            approx.addValue(350, 10);
            approx.addValue(400, -30);
            approx.addValue(450, 50);
            approx.addValue(500, 100);
            approx.addValue(550, 30);
            approx.addValue(600, 0);

            drawCurve(plot, 5, 0, 600);
        }

        private function drawCurve(target:Sprite, step:uint, fromArg:int, toArg:int):void 
        {
            var gfx:Graphics = target.graphics;
            gfx.clear();

            gfx.lineStyle(0, 0xCCCCCC, 1);
            gfx.moveTo(-50, 0);
            gfx.lineTo(50, 0);
            gfx.moveTo(0, -50);
            gfx.lineTo(0, 50);

            gfx.lineStyle(2, 0, 1);

            var minArg:int = Math.min(fromArg, toArg);
            var maxArg:int = Math.max(fromArg, toArg);

            if (step == 0) {
                step = 1;
            }

            var value:Number;
            for (var i:Number = minArg; i<=maxArg; i+=step) {
                value = approx.getApproximationValue(i);
                if (i) {
                    gfx.lineTo(i, value);
                } else {
                    gfx.moveTo(i, value);
                }
            }
        }

        private function onResize(event:Event = null):void
        {
            plot.x = 10;
            plot.y = stage.stageHeight/2;
        }
    }
}

Класс аппроксиматора

package org.noregret.math 
{
    import flash.geom.Point;
    import flash.utils.Dictionary;

    /**
     * @author Michael "Nox Noctis" Antipin (http://noregret.org)
     */
    public class LagrangeApproximator {

        private const points:Vector.<Point> = new Vector.<Point>();
        private const pointByArg:Dictionary = new Dictionary();

        private var isSorted:Boolean;

        public function LagrangeApproximator()
        {
        }

        public function addValue(argument:Number, value:Number):void
        {
            var point:Point;
            if (pointByArg[argument] != null) {
                trace("LagrangeApproximator.addValue("+arguments+"): ERROR duplicate function argument!");
                point = pointByArg[argument];
            } else {
                point = new Point();
                points.push(point);
                pointByArg[argument] = point;
            }
            point.x = argument;
            point.y = value;
            isSorted = false;
        }

        public function getApproximationValue(argument:Number):Number
        {
            if (!isSorted) {
                isSorted = true;
                points.sort(sortByArgument);
            }
            var listLength:uint = points.length;
            var point1:Point, point2:Point;
            var result:Number = 0;
            var coefficient:Number;
            for(var i:uint =0; i<listLength; i++) {
                coefficient = 1;
                point1 = points[i];
                for(var j:uint = 0; j<listLength; j++) {
                    if (i != j) {
                        point2 = points[j];
                        coefficient *= (argument-point2.x) / (point1.x-point2.x);
                    }
                }        
                result += point1.y * coefficient;
            }
            return result;
        }

        private function sortByArgument(a:Point, b:Point):int
        {
            if (a.x < b.x) {
                return -1;
            }
            if (a.x > b.x) {
                return 1;
            }            
            return 0;
        }

        public function get length():int
        {
            return points.length;            
        }

        public function clear():void
        {
            points.length = 0;
            var key:*;
            for (key in pointByArg) {
                delete pointByArg[key];
            }
        }
    }
}
person Michael Antipin    schedule 28.06.2011
comment
Это выглядит красиво, но я не могу установить этот SWC во Flash CS5 :( Было бы здорово, если бы это можно было решить как одно уравнение. Оно будет использоваться только как краткая визуализация заданных данных. - person MaRmAR; 28.06.2011
comment
Будут ли это очень конкретные данные или все время будут разные данные? - person Zéychin; 28.06.2011
comment
Я готов разработать для вас метод интерполяции Лагранжа, если данные всегда разные. Если (и только если!) вы просто рисуете один набор данных, а это всегда будут эти данные, я могу прогнать числа через расчет и дать вам очень хорошее приближение графика в одном уравнении. - person Zéychin; 28.06.2011
comment
просто примечание: кубические кривые Безье будут частью API рисования для Flash Player 11. - person Chunky Chunk; 28.06.2011
comment
@Zeychin: Спасибо, но это не единый набор данных. Я бы рисовал в Photoshop или Illustrator :) Это динамическая анимация во Flash. :/ - person MaRmAR; 29.06.2011
comment
@ TheDarkIn1978 Кривые Безье не проходят через каждую точку, не так ли? @MaRmAR Я так и предполагал. Вам помогло решение Nox Noctis? - person Zéychin; 30.06.2011