/// <summary>
        /// 2nd degree state variable lowpass notch or highpass notch filter
        /// 今田悟 and 深谷武彦,実用アナログフィルタ設計法,CQ出版,1989
        /// p.63 and p.58 Figure 2.15(a)
        /// </summary>
        private void DrawStateVariableNotchSecondOrderFilter(int nStage, SecondOrderComplexRationalPolynomial ps, double r, double c)
        {
            /*
             * 有理多項式は周波数が正規化されているので、まず係数を周波数スケーリングする。
             *
             * H(s) = { (s/ωc)^2 + n0 } { (s/ωc)^2 + d1*(s/ωc) + d0 }
             *
             *      ⇩  分子と分母にωc^2を掛ける。
             *
             * H(s) = { s^2 + ωc^2 n0 } { s^2 + ωc * d1 + ωc^2 d0 }
             *            ___
             * ωp = ωc * √d0
             *            ___
             * ωn = ωc * √n0
             *
             * Q = ωp / { d1 * ωc }
             *
             * ωp^2 = R2/{R3R6R7C1C2}
             *
             * ωn^2 = R8ωp^2/R9
             *
             * Q=(1+R4/R5)(1/{1/R1+1/R2+1/R3})sqrt(R6C1/R2R3R7C2)
             *
             * 設計法18
             *
             * R1=R2=R3=R4=R9=R10=R
             *
             * C1=C2=C
             *
             * Rf=1 / { ωp*Cf }
             *
             * R6=R7=Rf
             *
             * R5=R/{3Q-1}
             *
             * R8={ωn^2/ωp^2}R
             *
             */

            double ωc = CutoffFrequencyHz * 2.0 * Math.PI;
            double ωp = ωc * Math.Sqrt(ps.D(0).real);
            double ωn = ωc * Math.Sqrt(ps.N(0).real);
            double Q  = ωp / ps.D(1).real / ωc;

            /* Test p64 計算例18
             * Q = 5;
             * ωp = 2.0 * Math.PI * 1000;
             * ωn = 2.0 * Math.PI * 2000;
             */

            double rf = 1.0 / ωp / c;
            double r5 = r / (3.0 * Q - 1.0);
            double r8 = ωn * ωn * r / ωp / ωp;

            /*
             * Console.WriteLine("R={0} C={1} ωc={2} ωp={3} ωn={4} Q={5} Rf={6} R5={7} r8={8}",
             *  r, c, ωc, ωp, ωn, Q, rf, r5, r8);
             */

            textBoxParameters.Text += string.Format("Stage {0} is 2nd degree State variable lowpass notch filter. Fp={1}Hz Fn={2}Hz Q={3:G3}\n",
                                                    nStage + 1, ValueString(ωp / 2 / Math.PI), ValueString(ωn / 2 / Math.PI), Q);

            // 抵抗R1
            AddResistorH(mX, CIRCUIT_INPUT_LINE_H, ResistorValueString(r));

            {
                // R1の右から出る横線。オペアンプA1の入力につながる。
                var p = new List <Point>();
                p.Add(new Point(mX + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + RESISTOR_LENGTH + 30, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + RESISTOR_LENGTH + 30, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + RESISTOR_LENGTH + 30 + 10, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }

            mX += RESISTOR_LENGTH + 20;

            {
                // R1の右から出て、R2につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 20, CIRCUIT_INPUT_LINE_H - 30));
                AddLineStrip(p, mBrush);
            }
            // 抵抗R2
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H - 30, ResistorValueString(r));

            {
                // R2の左から出て、R3につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H - 60));
                p.Add(new Point(mX + 20, CIRCUIT_INPUT_LINE_H - 60));
                AddLineStrip(p, mBrush);
            }
            // 抵抗R3
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H - 60, ResistorValueString(r));

            {
                // オペアンプA1の左下から出て、R5につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX + 20, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H + 50));
                AddLineStrip(p, mBrush);
            }
            // 抵抗R5
            AddResistorV(mX, CIRCUIT_INPUT_LINE_H + 50, ResistorValueString(r5));

            // R5からGNDに接続する縦線。
            AddLine(mX, CIRCUIT_INPUT_LINE_H + 50 + RESISTOR_LENGTH,
                    mX, CIRCUIT_INPUT_LINE_H + 50 + RESISTOR_LENGTH + 20, mBrush);
            AddGnd(mX, CIRCUIT_INPUT_LINE_H + 50 + RESISTOR_LENGTH + 20);

            // R5の上からR4の左に接続する線。
            AddLine(mX, CIRCUIT_INPUT_LINE_H + 40,
                    mX + 20, CIRCUIT_INPUT_LINE_H + 40, mBrush);
            // 抵抗R4
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H + 40, ResistorValueString(r));

            mX += 20;

            // オペアンプA1
            AddOpamp(mX, CIRCUIT_INPUT_LINE_H, PlusPosition.Bottom);

            {
                // 抵抗R2からオペアンプA1の出力につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + OPAMP_WIDTH, CIRCUIT_INPUT_LINE_H));
                AddLineStrip(p, mBrush);
            }

            mX += OPAMP_WIDTH + 10;

            {
                // オペアンプA1出力 → R8入力

                var p = new List <Point>();
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H + 40 + 30));
                p.Add(new Point(mX + 20, CIRCUIT_INPUT_LINE_H + 40 + 30));
                AddLineStrip(p, mBrush);
            }
            // 抵抗R8
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H + 40 + 30, ResistorValueString(r8));

            // R2の右からR6の左に接続する線。
            AddLine(mX, CIRCUIT_INPUT_LINE_H - 30,
                    mX + 20, CIRCUIT_INPUT_LINE_H - 30, mBrush);
            // 抵抗R6
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H - 30, ResistorValueString(rf));

            // R6の右からC1の左に接続する線。
            AddLine(mX + 20 + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H - 30,
                    mX + 20 + RESISTOR_LENGTH + 40, CIRCUIT_INPUT_LINE_H - 30, mBrush);

            mX += 20 + RESISTOR_LENGTH + 20;

            // C1
            AddCapacitorH(mX + 20, CIRCUIT_INPUT_LINE_H - 30, CapacitorValueString(c));

            {
                // C1からオペアンプA2の出力につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + CAPACITOR_THICKNESS, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 10 + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 10 + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + 10 + OPAMP_WIDTH, CIRCUIT_INPUT_LINE_H));
                AddLineStrip(p, mBrush);
            }

            {
                // R6からオペアンプA2の左上につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 10, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }

            {
                // オペアンプA2の左下からGNDにつながる線。
                var p = new List <Point>();
                p.Add(new Point(mX + 10, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX, CIRCUIT_INPUT_LINE_H + 20));
                AddLineStrip(p, mBrush);
            }

            // オペアンプA2
            AddOpamp(mX + 10, CIRCUIT_INPUT_LINE_H, PlusPosition.Bottom);
            AddGnd(mX, CIRCUIT_INPUT_LINE_H + 20);

            mX += 10 + OPAMP_WIDTH;

            {
                // R4の右からオペアンプA2の出力につながる線。
                var p = new List <Point>();
                p.Add(new Point(mX - OPAMP_WIDTH - 10 - 20 - RESISTOR_LENGTH - 20 - 10 - OPAMP_WIDTH + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H + 40));
                p.Add(new Point(mX + 10, CIRCUIT_INPUT_LINE_H + 40));
                p.Add(new Point(mX + 10, CIRCUIT_INPUT_LINE_H));
                AddLineStrip(p, mBrush);
            }

            mX += 10;

            // オペアンプの出力からR7
            AddLine(mX, CIRCUIT_INPUT_LINE_H - 30,
                    mX + 20, CIRCUIT_INPUT_LINE_H - 30, mBrush);

            // 抵抗R7
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H - 30, ResistorValueString(rf));

            // R7 → C2
            AddLine(mX + 20 + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H - 30,
                    mX + 20 + RESISTOR_LENGTH + 40, CIRCUIT_INPUT_LINE_H - 30, mBrush);

            // C2
            AddCapacitorH(mX + 20 + RESISTOR_LENGTH + 40, CIRCUIT_INPUT_LINE_H - 30, CapacitorValueString(c));

            {
                // R7 → オペアンプA3の左上
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 30, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }
            {
                // オペアンプA3の左下 → GND
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 30, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H + 20));
                AddLineStrip(p, mBrush);
            }

            mX += 20 + RESISTOR_LENGTH + 30;

            // オペアンプA3
            AddOpamp(mX, CIRCUIT_INPUT_LINE_H, PlusPosition.Bottom);
            AddGnd(mX - 10, CIRCUIT_INPUT_LINE_H + 20);

            {
                // C2の出力 → オペアンプA3の出力
                var p = new List <Point>();
                p.Add(new Point(mX + 10 + CAPACITOR_THICKNESS, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + OPAMP_WIDTH, CIRCUIT_INPUT_LINE_H));
                AddLineStrip(p, mBrush);
            }

            {
                // R3の出力 → オペアンプA3の出力
                var p = new List <Point>();
                p.Add(new Point(mX - 10 - 20 - RESISTOR_LENGTH - 20 - 10 - OPAMP_WIDTH - 10 - 20 - RESISTOR_LENGTH - 20 - 10 - OPAMP_WIDTH - 20 + 20 + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H - 60));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 60));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                AddLineStrip(p, mBrush);
            }

            mX += OPAMP_WIDTH + 10;

            // オペアンプA3 → R9
            AddLine(mX, CIRCUIT_INPUT_LINE_H,
                    mX + 20, CIRCUIT_INPUT_LINE_H, mBrush);
            // 抵抗R9
            AddResistorH(mX + 20, CIRCUIT_INPUT_LINE_H, ResistorValueString(r));

            {
                // R9の出力 → R10入力
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 20, CIRCUIT_INPUT_LINE_H - 30));
                AddLineStrip(p, mBrush);
            }
            {
                // R9の出力 → オペアンプA4左上
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 10, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 10, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 20, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }
            {
                // オペアンプA4左下 → GND
                var p = new List <Point>();
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 20, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 10, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 20 + RESISTOR_LENGTH + 20 + 10, CIRCUIT_INPUT_LINE_H + 20));
                AddLineStrip(p, mBrush);
            }

            // 抵抗R10
            AddResistorH(mX + 20 + RESISTOR_LENGTH + 20 + 20, CIRCUIT_INPUT_LINE_H - 30, ResistorValueString(r));

            mX += 20 + RESISTOR_LENGTH + 40;

            // オペアンプA4
            AddOpamp(mX, CIRCUIT_INPUT_LINE_H, PlusPosition.Bottom);
            AddGnd(mX - 10, CIRCUIT_INPUT_LINE_H + 20);

            {
                // R8の出力 → オペアンプA4左上入力
                var p = new List <Point>();
                p.Add(new Point(mX - 30, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX - 30, CIRCUIT_INPUT_LINE_H + 40 + 30));
                p.Add(new Point(mX - 40 - RESISTOR_LENGTH - 20 - 10 - OPAMP_WIDTH - 10 - 20 - RESISTOR_LENGTH - 20 - 10 - OPAMP_WIDTH - 10 - 20, CIRCUIT_INPUT_LINE_H + 40 + 30));
                AddLineStrip(p, mBrush);
            }

            {
                // R10出力 → オペアンプA4出力
                var p = new List <Point>();
                p.Add(new Point(mX + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + OPAMP_WIDTH, CIRCUIT_INPUT_LINE_H));
                AddLineStrip(p, mBrush);
            }

            // オペアンプA4出力 → 出力
            AddLine(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H,
                    mX + OPAMP_WIDTH + 70, CIRCUIT_INPUT_LINE_H, mBrush);

            mX += OPAMP_WIDTH + 70;
        }
        /// <summary>
        /// Sallen-Key Minimum Sensitivity 2nd degree Lowpass filter
        /// Analog Electronic Filters pp.470
        /// </summary>
        private void DrawSallenKeyLowpassSecondOrderFilter(int nStage, SecondOrderComplexRationalPolynomial ps, double resistorValue)
        {
            /* k=1
             * R1=R2=1
             * とする。
             *
             * C1=2Q/ω0
             *
             * C2=1/2ω0Q
             *
             *       ___
             * ω0 = √d0
             * Q = ω0/d1
             */
            double ω0 = Math.Sqrt(ps.D(0).real);
            double Q  = ω0 / ps.D(1).real;
            double r1 = 1;
            double r2 = 1;
            double c1 = 2.0 * Q / ω0;
            double c2 = 1.0 / 2.0 / ω0 / Q;

            // 周波数スケーリング。キャパシタの値をωcで割る。
            double ωc = CutoffFrequencyHz * 2.0 * Math.PI;

            c1 /= ωc;
            c2 /= ωc;

            // 最後に抵抗値を全て10 * 1000倍、キャパシターの容量を10*1000分の一する。
            r1 *= resistorValue;
            r2 *= resistorValue;
            c1 /= resistorValue;
            c2 /= resistorValue;

            string message = "";

            if (4.0 <= Q)
            {
                message = " : HighQ and unstable";
            }
            textBoxParameters.Text += string.Format("Stage {0} is Sallen-Key LPF. ω0={1:G3} Q={2:G3}{3}\n", nStage + 1,
                                                    ω0, Q, message);

            // 抵抗R1
            AddResistorH(mX, CIRCUIT_INPUT_LINE_H, ResistorValueString(r1));

            // R1の右から出る横線。R2の入力につながる。
            AddLine(mX + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H,
                    mX + RESISTOR_LENGTH + 40, CIRCUIT_INPUT_LINE_H, mBrush);

            mX += RESISTOR_LENGTH + 40;

            // 抵抗R2
            AddResistorH(mX, CIRCUIT_INPUT_LINE_H, ResistorValueString(r2));

            {
                // 抵抗R2の右から出る横線。オペアンプの+入力につながる。
                var p = new List <Point>();
                p.Add(new Point(mX + RESISTOR_LENGTH, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + RESISTOR_LENGTH + 20 + 40, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX + RESISTOR_LENGTH + 20 + 40, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + RESISTOR_LENGTH + 20 + 40 + 10, CIRCUIT_INPUT_LINE_H - OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }

            {
                // 抵抗R1-R2とキャパシタC1をつなげる縦線。
                var p = new List <Point>();
                p.Add(new Point(mX - 20, CIRCUIT_INPUT_LINE_H));
                p.Add(new Point(mX - 20, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + RESISTOR_LENGTH + 20, CIRCUIT_INPUT_LINE_H - 30));
                AddLineStrip(p, mBrush);
            }

            mX += RESISTOR_LENGTH + 20;

            // キャパシターC1
            AddCapacitorH(mX, CIRCUIT_INPUT_LINE_H - 30, CapacitorValueString(c1));

            {
                // キャパシターC1からオペアンプのー入力。
                var p = new List <Point>();
                p.Add(new Point(mX + CAPACITOR_THICKNESS, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 40 + 10 + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H - 30));
                p.Add(new Point(mX + 40 + 10 + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H + 30));
                p.Add(new Point(mX + 40, CIRCUIT_INPUT_LINE_H + 30));
                p.Add(new Point(mX + 40, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                p.Add(new Point(mX + 40 + 10, CIRCUIT_INPUT_LINE_H + OPAMP_INPUT_H_OFFS));
                AddLineStrip(p, mBrush);
            }


            // R2からC2に接続する縦線。
            AddLine(mX, CIRCUIT_INPUT_LINE_H,
                    mX, CIRCUIT_INPUT_LINE_H + 30, mBrush);

            // キャパシターC2
            AddCapacitorV(mX, CIRCUIT_INPUT_LINE_H + 30, CapacitorValueString(c2));

            // C2からGNDに接続する縦線。
            AddLine(mX, CIRCUIT_INPUT_LINE_H + 30 + CAPACITOR_THICKNESS,
                    mX, CIRCUIT_INPUT_LINE_H + 30 + CAPACITOR_THICKNESS + RESISTOR_LENGTH + 10, mBrush);

            AddGnd(mX, CIRCUIT_INPUT_LINE_H + 30 + CAPACITOR_THICKNESS + RESISTOR_LENGTH + 10);

            mX += 40 + 10;

            AddOpamp(mX, CIRCUIT_INPUT_LINE_H);

            // オペアンプの出力線。
            AddLine(mX + OPAMP_WIDTH, CIRCUIT_INPUT_LINE_H,
                    mX + OPAMP_WIDTH + 50, CIRCUIT_INPUT_LINE_H, mBrush);

            AddFilledCircle(mX + OPAMP_WIDTH + 10, CIRCUIT_INPUT_LINE_H, 5, mBrush);

            mX += OPAMP_WIDTH + 50;
        }