lipsync/trunk/LipSync/Common/CurveEditor.cs

2654 lines
113 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* CurveEditor.cs
* Copyright (c) 2007-2009 kbinani
*
* This file is part of LipSync.
*
* LipSync is free software; you can redistribute it and/or
* modify it under the terms of the BSD License.
*
* LipSync is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Windows.Forms;
using Boare.Lib.AppUtil;
using LipSync;
namespace CurveEditor {
public partial class CurveEditor : UserControl {
public delegate void CurveEditedEventHandler();
public enum MouseDownMode {
Nothing,
MovePoint,
}
private class ScaleAndOffset {
public float Scale;
public float Offset;
public ScaleAndOffset( float scale, float offset ) {
Scale = scale;
Offset = offset;
}
}
Dictionary<string, BezierChain> m_list = new Dictionary<string, BezierChain>();
Dictionary<string, ScaleAndOffset> m_list_minmax = new Dictionary<string, ScaleAndOffset>();
bool m_drag_ = false;
bool m_drag_with_space_key = false;
Point m_drag_start;
float m_old_xoffset;
float m_old_yoffset;
const int LABEL_WIDTH = 15;
const int LIST_WIDTH = 60;
string m_selected = "";
int m_picked_point_id = -1;
PickedSide m_picked_side;
bool m_scale_locked = false;
ControlType m_last_added_type = ControlType.None;
List<Command> m_commands = new List<Command>();
int m_command_position = -1;
PointF m_before_edit;
//bool m_moved = false;
//bool m_mouse_upped = true;
bool m_change_xscale = false;
bool m_change_yscale = false;
int m_change_origin;
float m_old_scale;
float CHANGE_SCALE_ORDER = 70f;
bool m_spacekey_down = false;
Cursor HAND;
bool _number_visible = false;
PointF _number;
Font _font;
Point _mouse_position;
ContextMenuStrip _cmenu;
ToolStripMenuItem _cmenuNumericInput;
bool m_rescaley_enabled = true;
XLabel m_xlabel = XLabel.None;
YLabel m_ylabel = YLabel.None;
bool m_change_xscale_with_wheel = true;
bool m_change_yscale_with_wheel = true;
bool m_show_list = true;
float m_max_xscale = 100f;
float m_min_xscale = 1e-4f;
float m_max_yscale = 100f;
float m_min_yscale = 1e-4f;
Color m_origin_scale_line = Color.FromArgb( 44, 44, 44 );
Color m_scale_line = Color.FromArgb( 94, 94, 94 );
Color m_sub_scale_line = Color.FromArgb( 110, 110, 110 );
Color m_cHandle_master = Color.FromArgb( 240, 144, 160 );
Color m_cControl_master = Color.FromArgb( 255, 130, 0 );
Color m_cHandle_normal = Color.FromArgb( 255, 130, 0 );
Color m_cControl_normal = Color.FromArgb( 51, 192, 64 );
Color m_data_point = Color.Black;
Color m_data_point_hilight = Color.Red;
int m_data_point_size = 2;
int m_control_point_size = 2;
PointType m_data_point_type;
PointType m_control_point_type;
Color m_label_back = Color.FromArgb( 172, 172, 172 );
Color m_list_back = Color.FromArgb( 143, 143, 143 );
float m_xscale = 1f;
float m_yscale = 1f;
float m_xoffset = 0f;
float m_yoffset = 0f;
int m_place_count_x = 1;
int m_place_count_y = 1;
bool m_scroll_enabled = true;
bool m_mouse_moved = false;
MouseDownMode m_mouse_down_mode = MouseDownMode.Nothing;
/// <summary>
/// カーブのデータ点・制御点などが編集された時に発生します
/// </summary>
public event CurveEditedEventHandler CurveEdited;
/// <summary>
/// パブリックコンストラクタ
/// </summary>
public CurveEditor() {
InitializeComponent();
this.MouseWheel += new MouseEventHandler( CurveEditor_MouseWheel );
this.SetStyle( ControlStyles.DoubleBuffer, true );
this.SetStyle( ControlStyles.UserPaint, true );
this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
byte[] foo = new byte[] { 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x20, 0x20, 0x0, 0x0, 0x10, 0x0, 0x10, 0x0, 0xe8, 0x2, 0x0, 0x0, 0x16, 0x0,
0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0,
0x0, 0x0, 0x80, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0,
0x80, 0x0, 0x80, 0x0, 0x80, 0x0, 0x0, 0x80, 0x80, 0x0, 0xc0, 0xc0, 0xc0, 0x0, 0x80, 0x80, 0x80, 0x0, 0xff, 0x0,
0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0xff,
0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xf0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xf8,
0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xcf, 0xf3, 0xff, 0xff, 0x87, 0xe1, 0xff, 0xff, 0x7,
0xe0, 0xff, 0xff, 0x7, 0xe0, 0xff, 0xff, 0x87, 0xe1, 0xff, 0xff, 0xcf, 0xf3, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xf8,
0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, };
using ( MemoryStream ms = new MemoryStream( foo ) ) {
HAND = new Cursor( ms );
}
_font = new Font( "MS UI Gothic", 10 );
}
public bool GetYScaleAndYOffset( string ID, out float y_scale, out float y_offset ) {
if ( m_list_minmax.ContainsKey( ID ) ) {
y_scale = m_list_minmax[ID].Scale;
y_offset = m_list_minmax[ID].Offset;
return true;
} else {
y_scale = 1f;
y_offset = 0f;
return false;
}
}
public void SetYScaleAndYOffset( string ID, float y_scale, float y_offset ) {
if ( m_list_minmax.ContainsKey( ID ) ) {
m_list_minmax[ID].Scale = y_scale;
m_list_minmax[ID].Offset = y_offset;
this.Invalidate();
}
}
public static string _( string s ) {
return Messaging.GetMessage( s );
}
public Size GraphSize {
get {
int width, height;
if ( XLabel != XLabel.None ) {
height = this.Height - LABEL_WIDTH;
} else {
height = this.Height;
}
if ( YLabel != YLabel.None ) {
width = this.Width - LABEL_WIDTH;
} else {
width = this.Width;
}
if ( ShowList ) {
width -= LIST_WIDTH;
}
return new Size( width, height );
}
}
/// <summary>
/// 縦軸スケールの変更を許可するかどうかを取得または設定します
/// </summary>
public bool RescaleYEnabled {
get {
return m_rescaley_enabled;
}
set {
m_rescaley_enabled = value;
}
}
bool Drag {
get {
return m_drag_;
}
set {
m_drag_ = value;
if ( m_drag_ ) {
this.Cursor = HAND;
} else {
this.Cursor = Cursors.Default;
}
}
}
/// <summary>
/// 現在カーブエディタに設定されているデータを全て破棄し,初期化します
/// </summary>
public void Clear() {
m_list.Clear();
m_list_minmax.Clear();
m_commands.Clear();
m_command_position = -1;
m_selected = "";
m_picked_point_id = -1;
}
/// <summary>
/// Undo, Redo用のバッファを全て破棄し初期化します
/// </summary>
public void ClearBuffer() {
m_commands.Clear();
m_command_position = -1;
}
/// <summary>
/// 横軸の目盛の種類を取得または設定します
/// </summary>
public XLabel XLabel {
get {
return m_xlabel;
}
set {
m_xlabel = value;
}
}
/// <summary>
/// 縦軸の目盛の種類を取得または設定します
/// </summary>
public YLabel YLabel {
get {
return m_ylabel;
}
set {
m_ylabel = value;
}
}
/// <summary>
/// マウスホイールで横軸のスケールを変更するかどうかを取得または設定します
/// </summary>
public bool ChangeXScaleWithWheel {
get {
return m_change_xscale_with_wheel;
}
set {
m_change_xscale_with_wheel = value;
}
}
/// <summary>
/// マウスホイールで縦軸のスケールを変更するかどうかを取得または設定します
/// </summary>
public bool ChangeYScaleWithWheel {
get {
return m_change_yscale_with_wheel;
}
set {
m_change_yscale_with_wheel = value;
}
}
/// <summary>
/// 編集対象となるカーブのリストを,画面右側に表示するかどうかを取得または設定します
/// </summary>
public bool ShowList {
get {
return m_show_list;
}
set {
m_show_list = value;
}
}
/// <summary>
/// 横軸のスケールとして設定できる最大値を取得または設定します
/// </summary>
public float MaxXScale {
get {
return m_max_xscale;
}
set {
if ( value > 0 && value > m_min_xscale ) {
m_max_xscale = value;
}
}
}
/// <summary>
/// 横軸のスケールとして設定できる最小値を取得または設定します
/// </summary>
public float MinXScale {
get {
return m_min_xscale;
}
set {
if ( value > 0 && value < m_max_xscale ) {
m_min_xscale = value;
}
}
}
/// <summary>
/// 縦軸のスケールとして設定できる最大値を取得または設定します
/// </summary>
public float MaxYScale {
get {
return m_max_yscale;
}
set {
if ( value > 0 && value > m_min_yscale ) {
m_max_yscale = value;
}
}
}
/// <summary>
/// 縦軸のスケールとして設定できる最小値を取得または設定します
/// </summary>
public float MinYScale {
get {
return m_min_yscale;
}
set {
if ( value > 0 && value < m_max_yscale ) {
m_min_yscale = value;
}
}
}
void CurveEditor_MouseWheel( object sender, MouseEventArgs e ) {
Keys modifier = Control.ModifierKeys;
if ( modifier == Keys.Control ) {
float dx = e.Delta / XScale;
XOffset -= dx;
this.Invalidate();
} else if ( modifier == Keys.Shift ) {
float dy = e.Delta / YScale;
YOffset -= dy;
if ( m_selected != "" ) {
m_list_minmax[m_selected].Offset = YOffset;
}
this.Invalidate();
} else if ( modifier == Keys.None ) {
float count = e.Delta / 120f;
float n_xscale;
float n_yscale;
float order;
if ( count < 0 ) {
order = (float)Math.Pow( 0.83, Math.Abs( count ) );
} else {
order = (float)(1f / Math.Pow( 0.83, Math.Abs( count ) ));
}
if ( m_change_xscale_with_wheel ) {
n_xscale = m_xscale * order;
} else {
n_xscale = m_xscale;
}
if ( m_change_yscale_with_wheel ) {
n_yscale = m_yscale * order;
} else {
n_yscale = m_yscale;
}
if ( m_xscale != n_xscale || m_yscale != n_yscale ) {
bool changed = false;
if ( MinXScale <= n_xscale && n_xscale <= MaxXScale ) {
XOffset = e.X * (1f / n_xscale - 1f / XScale) + XOffset;
XScale = n_xscale;
changed = true;
}
if ( MinYScale <= n_yscale && n_yscale <= MaxYScale ) {
YOffset = (this.Height - e.Y) * (1f / n_yscale - 1f / YScale) + YOffset;
YScale = n_yscale;
changed = true;
}
if ( changed ) {
this.Invalidate();
}
}
}
}
/// <summary>
/// 編集対象となるカーブを,このコントロールに追加します
/// </summary>
/// <param name="ID"></param>
/// <param name="curve"></param>
/*public void Add( string ID, Color curve ) {
m_list.Add( ID, new BezierChain( curve ) );
}*/
public void Add( string ID, BezierChain chain ) {
m_list.Add( ID, chain );
m_list_minmax.Add( ID, new ScaleAndOffset( 1f, 0f ) );
}
//todo:動かしうるxの範囲を探知できるようにするpt1-4それぞれが動く場合。
/// <summary>
/// 4つの制御点からなるベジエ曲線が、x軸について陰かどうかを判定する
/// </summary>
/// <param name="pt1">始点</param>
/// <param name="pt2">制御点1</param>
/// <param name="pt3">制御点2</param>
/// <param name="pt4">終点</param>
/// <returns></returns>
public static bool IsBezierImplicit( float pt1, float pt2, float pt3, float pt4 ) {
double a = pt4 - 3 * pt3 + 3 * pt2 - pt1;
double b = 2 * pt3 - 4 * pt2 + 2 * pt1;
double c = pt2 - pt1;
if ( a == 0 ) {
if ( c >= 0 && b + c >= 0 ) {
return true;
} else {
return false;
}
} else if ( a > 0 ) {
if ( -b / (2 * a) <= 0 ) {
if ( c >= 0 ) {
return true;
} else {
return false;
}
} else if ( 1 <= -b / (2 * a) ) {
if ( a + b + c >= 0 ) {
return true;
} else {
return false;
}
} else {
if ( c - b * b / (4 * a) >= 0 ) {
return true;
} else {
return false;
}
}
} else {
if ( -b / (2 * a) <= 0.5 ) {
if ( a + b + c >= 0 ) {
return true;
} else {
return false;
}
} else {
if ( c >= 0 ) {
return true;
} else {
return false;
}
}
}
}
#region
public Color MainScaleLine {
get {
return m_origin_scale_line;
}
set {
m_origin_scale_line = value;
}
}
public Color ScaleLine {
get {
return m_scale_line;
}
set {
m_scale_line = value;
}
}
public Color SubScaleLine {
get {
return m_sub_scale_line;
}
set {
m_sub_scale_line = value;
}
}
public Color HandleMaster {
get {
return m_cHandle_master;
}
set {
m_cHandle_master = value;
}
}
public Color ControlMaster {
get {
return m_cControl_master;
}
set {
m_cControl_master = value;
}
}
public Color HandleNormal {
get {
return m_cHandle_normal;
}
set {
m_cHandle_normal = value;
}
}
public Color ControlNormal {
get {
return m_cControl_normal;
}
set {
m_cControl_normal = value;
}
}
/// <summary>
/// データポイントの描画色を取得または設定します
/// </summary>
public Color DataPoint {
get {
return m_data_point;
}
set {
m_data_point = value;
}
}
/// <summary>
/// 選択されたデータポイントの描画色を取得または設定します
/// </summary>
public Color DataPointHilight {
get {
return m_data_point_hilight;
}
set {
m_data_point_hilight = value;
}
}
public int DataPointSize {
get {
return m_data_point_size;
}
set {
m_data_point_size = value;
}
}
public int ControlPointSize {
get {
return m_control_point_size;
}
set {
m_control_point_size = value;
}
}
public PointType DataPointType {
get {
return m_data_point_type;
}
set {
m_data_point_type = value;
}
}
public PointType ControlPointType {
get {
return m_control_point_type;
}
set {
m_control_point_type = value;
}
}
public Color LabelBackground {
get {
return m_label_back;
}
set {
m_label_back = value;
}
}
public Color ListBackground {
get {
return m_list_back;
}
set {
m_list_back = value;
}
}
#endregion
/// <summary>
/// 横軸のスケールを取得または設定します[pixel/(任意の単位)]
/// </summary>
public float XScale {
get {
return m_xscale;
}
set {
if ( !m_scale_locked && MinXScale <= value && value <= MaxXScale ) {
m_xscale = value;
}
}
}
/// <summary>
/// 縦軸のスケールを取得または設定します[pixel/(任意の単位)]
/// </summary>
public float YScale {
get {
return m_yscale;
}
set {
if ( !m_scale_locked && MinYScale <= value && value <= MaxYScale ) {
m_yscale = value;
}
}
}
/// <summary>
/// 画面左端におけるx軸の値を取得または設定します
/// </summary>
public float XOffset {
get {
return m_xoffset;
}
set {
m_xoffset = value;
}
}
/// <summary>
///
/// </summary>
public float YOffset {
get {
return m_yoffset;
}
set {
m_yoffset = value;
}
}
/// <summary>
/// グラフ上のX座標をコントロール上の座標(pixel)に換算します
/// </summary>
/// <param name="sX"></param>
/// <returns></returns>
private int cX( float sX ) {
return (int)((sX + XOffset) * XScale);
}
/// <summary>
/// グラフ上のY座標をコントロール上の座標(pixel)に換算します
/// </summary>
/// <param name="sY"></param>
/// <returns></returns>
private int cY( float sY ) {
return this.Height - (int)((sY + YOffset) * YScale);
}
private Point cPoint( PointF sPoint ) {
return new Point( cX( sPoint.X ), cY( sPoint.Y ) );
}
private float sX( int cX ) {
return cX / XScale - XOffset;
}
private float sY( int cY ) {
return (this.Height - cY) / YScale - YOffset;
}
private PointF sPoint( Point cPoint ) {
return new PointF( sX( cPoint.X ), sY( cPoint.Y ) );
}
private float m_place_x {
get {
int n1 = (int)Math.Log10( 400 / XScale );
int n2 = (int)Math.Log10( 200 / XScale );
int n5 = (int)Math.Log10( 80 / XScale );
float d1 = (float)Math.Pow( 10, n1 );
float d2 = (float)Math.Pow( 10, n2 );
float d5 = (float)Math.Pow( 10, n5 );
if ( d1 <= 2 * d2 && d1 <= 5 * d5 ) {
m_place_count_x = 1;
return d1;
} else if ( 2 * d2 <= d1 && 2 * d2 <= 5 * d5 ) {
m_place_count_x = 2;
return d2;
} else {
m_place_count_x = 5;
return d5;
}
}
}
private float m_place_y {
get {
int n1 = (int)Math.Log10( 400 / YScale );
int n2 = (int)Math.Log10( 200 / YScale );
int n5 = (int)Math.Log10( 80 / YScale );
float d1 = (float)Math.Pow( 10, n1 );
float d2 = (float)Math.Pow( 10, n2 );
float d5 = (float)Math.Pow( 10, n5 );
if ( d1 <= 2 * d2 && d1 <= 5 * d5 ) {
m_place_count_y = 1;
return d1;
} else if ( 2 * d2 <= d1 && 2 * d2 <= 5 * d5 ) {
m_place_count_y = 2;
return d2;
} else {
m_place_count_y = 5;
return d5;
}
}
}
public BezierChain this[string ID] {
get {
return m_list[ID];
}
set {
m_list[ID] = value;
}
}
public float this[string ID, float x] {
get {
return m_list[ID].GetValue( x );
}
}
public bool ScrollEnabled {
get {
return m_scroll_enabled;
}
set {
m_scroll_enabled = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>関数を抜けるとき確実にm_scale_locked = falseとすること</remarks>
private void CurveEditor_Paint( object sender, PaintEventArgs e ) {
m_scale_locked = true;
//グラフ内のメモリを描画
int o_x = cX( 0f );
int o_y = cY( 0f );
e.Graphics.DrawLine( new Pen( MainScaleLine ), new Point( 0, o_y ), new Point( this.Width, o_y ) );
e.Graphics.DrawLine( new Pen( MainScaleLine ), new Point( o_x, 0 ), new Point( o_x, this.Height ) );
#if DEBUG
//MessageBox.Show( "place_x=" + place_x );
#endif
float place_x = m_place_count_x * m_place_x;
int start_x = (int)(sX( 0 ) / place_x) - 1;
int end_x = (int)(sX( this.Width ) / place_x) + 1;
for ( int i = start_x; i <= end_x; i++ ) {
float sx;
int px;
if ( i != 0 ) {
sx = i * place_x;
px = cX( sx );
e.Graphics.DrawLine( new Pen( ScaleLine ), new Point( px, 0 ), new Point( px, this.Height ) );
}
sx = (i + 0.5f) * place_x;
px = cX( sx );
e.Graphics.DrawLine( new Pen( SubScaleLine ), new Point( px, 0 ), new Point( px, this.Height ) );
}
float place_y = m_place_count_y * m_place_y;
int start_y = (int)(sY( this.Height ) / place_y) - 1;
int end_y = (int)(sY( 0 ) / place_y) + 1;
for ( int i = start_y; i <= end_y; i++ ) {
float sy;
int py;
if ( i != 0 ) {
sy = i * place_y;
py = cY( sy );
e.Graphics.DrawLine( new Pen( ScaleLine ), new Point( 0, py ), new Point( this.Width, py ) );
}
sy = (i + 0.5f) * place_y;
py = cY( sy );
e.Graphics.DrawLine( new Pen( SubScaleLine ), new Point( 0, py ), new Point( this.Width, py ) );
}
//foreach ( BezierChain chain in m_list.Values ) {
foreach ( string ID in m_list.Keys ) {
BezierChain chain = m_list[ID];
BezierPoint last;
if ( chain.Count >= 2 ) {
last = chain.List[0];
} else {
int default_y = cY( chain.Default );
e.Graphics.DrawLine( new Pen( chain.Color ), new Point( 0, default_y ), new Point( this.Width, default_y ) );
if ( ID == m_selected && chain.Count >= 1 ) {
int width2 = m_data_point_size;
switch ( DataPointType ) {
case PointType.Circle:
if ( chain.List[0].ID == m_picked_point_id ) {
e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
} else {
e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
}
break;
case PointType.Rectangle:
if ( chain.List[0].ID == m_picked_point_id ) {
e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
} else {
e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
}
break;
}
}
continue;
}
int width = m_data_point_size;
if ( ID == m_selected ) {
switch ( DataPointType ) {
case PointType.Circle:
if ( chain.List[0].ID == m_picked_point_id ) {
e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
} else {
e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
}
break;
case PointType.Rectangle:
if ( chain.List[0].ID == m_picked_point_id ) {
e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
} else {
e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
}
break;
}
}
//デフォルト値(左側)の描画
if ( chain.Count >= 1 ) {
int default_y = cY( chain.Default );
int x = cX( chain.List[0].Base.X );
int y = cY( chain.List[0].Base.Y );
e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( -1, default_y ) );
e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( x, y ) );
}
//for ( int i = 1; i < chain.Count; i++ ) {
bool first = true;
Size sz = new Size( 2 * width, 2 * width );
for( int i = 0; i < chain.List.Count; i++ ){
BezierPoint bp = chain.List[i];
if ( first ) {
last = bp;
first = false;
continue;
}
//if ( last.ControlRightType != ControlType.None && chain[i].ControlLeftType != ControlType.None ) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
e.Graphics.DrawBezier( new Pen( chain.Color ),
cPoint( last.Base ),
cPoint( last.ControlRight ),
cPoint( bp.ControlLeft ),
cPoint( bp.Base ) );
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
//制御ハンドル用の線
if ( ID == m_selected ) {
if ( bp.ControlLeftType == ControlType.Master ) {
e.Graphics.DrawLine( new Pen( HandleMaster ), cPoint( bp.Base ), cPoint( bp.ControlLeft ) );
} else if ( bp.ControlLeftType == ControlType.Normal ) {
e.Graphics.DrawLine( new Pen( HandleNormal ), cPoint( bp.Base ), cPoint( bp.ControlLeft ) );
}
if ( last.ControlRightType == ControlType.Master ) {
e.Graphics.DrawLine( new Pen( HandleMaster ), cPoint( last.Base ), cPoint( last.ControlRight ) );
} else if ( last.ControlRightType == ControlType.Normal ) {
e.Graphics.DrawLine( new Pen( HandleNormal ), cPoint( last.Base ), cPoint( last.ControlRight ) );
}
//データ点
width = m_data_point_size;
Point data_point = new Point( cX( bp.Base.X ) - width, cY( bp.Base.Y ) - width );
switch ( DataPointType ) {
case PointType.Circle:
if ( bp.ID == m_picked_point_id ) {
e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
new Rectangle( data_point, sz ) );
} else {
e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
new Rectangle( data_point, sz ) );
}
break;
case PointType.Rectangle:
if ( bp.ID == m_picked_point_id ) {
e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
new Rectangle( data_point, sz ) );
} else {
e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
new Rectangle( data_point, sz ) );
}
break;
}
//制御ハンドル点
width = m_control_point_size;
Color cLeft = Color.Black;
Color cRight = Color.Black;
if ( bp.ControlLeftType == ControlType.Master ) {
cLeft = ControlMaster;
} else if ( bp.ControlLeftType == ControlType.Normal ) {
cLeft = ControlNormal;
}
if ( last.ControlRightType == ControlType.Master ) {
cRight = ControlMaster;
} else if ( last.ControlRightType == ControlType.Normal ) {
cRight = ControlNormal;
}
if ( bp.ControlLeftType != ControlType.None && (bp.ControlLeft.X != bp.Base.X || bp.ControlLeft.Y != bp.Base.Y) ) {
Point ctrl_left = new Point( cX( bp.ControlLeft.X ) - width, cY( bp.ControlLeft.Y ) - width );
switch ( ControlPointType ) {
case PointType.Circle:
e.Graphics.FillEllipse( new SolidBrush( cLeft ), new Rectangle( ctrl_left, sz ) );
break;
case PointType.Rectangle:
e.Graphics.FillRectangle( new SolidBrush( cLeft ), new Rectangle( ctrl_left, sz ) );
break;
}
}
if ( last.ControlRightType != ControlType.None && (last.ControlRight.X != last.Base.X || last.ControlRight.Y != last.Base.Y) ) {
Point ctrl_right = new Point( cX( last.ControlRight.X ) - width, cY( last.ControlRight.Y ) - width );
switch ( ControlPointType ) {
case PointType.Circle:
e.Graphics.FillEllipse( new SolidBrush( cRight ), new Rectangle( ctrl_right, sz ) );
break;
case PointType.Rectangle:
e.Graphics.FillRectangle( new SolidBrush( cRight ), new Rectangle( ctrl_right, sz ) );
break;
}
}
}
last = bp;
}
//デフォルト値(右側)の描画
if ( chain.Count >= 1 ) {
int default_y = cY( chain.Default );
int x = cX( last.Base.X );
int y = cY( last.Base.Y );
e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( this.Width + 1, default_y ) );
e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( x, y ) );
}
}
// マウス位置の数値を描画
if ( _number_visible ) {
int x = cX( _number.X );
int y = cY( _number.Y );
e.Graphics.DrawString( "x : " + _number.X,
_font,
Brushes.Black,
new PointF( x, y - 24 ) );
e.Graphics.DrawString( "y : " + _number.Y,
_font,
Brushes.Black,
new PointF( x, y - 12 ) );
} else {
float x = sX( _mouse_position.X );
float y = sY( _mouse_position.Y );
e.Graphics.DrawString( "x : " + x,
_font,
Brushes.Black,
new PointF( _mouse_position.X, _mouse_position.Y - 24 ) );
e.Graphics.DrawString( "y : " + y,
_font,
Brushes.Black,
new PointF( _mouse_position.X, _mouse_position.Y - 12 ) );
}
int label_y = 0;
using ( Font font = new Font( "MS UI Gothic", 9 ) ) {
//ラベルを描画。(必要なら)
switch ( XLabel ) {
case XLabel.Top:
e.Graphics.FillRectangle( new SolidBrush( LabelBackground ),
new Rectangle( 0, 0, this.Width, LABEL_WIDTH ) );
break;
case XLabel.Bottom:
e.Graphics.FillRectangle( new SolidBrush( LabelBackground ),
new Rectangle( 0, this.Height - LABEL_WIDTH, this.Width, LABEL_WIDTH ) );
label_y = this.Height - LABEL_WIDTH;
break;
}
if ( XLabel != XLabel.None ) {
for ( int i = start_x; i <= end_x; i++ ) {
float sx = i * place_x;
int px = cX( sx );
e.Graphics.DrawString( sx.ToString(), font, Brushes.Black, new PointF( px, label_y ) );
}
}
int label_x = 0;
switch ( YLabel ) {
case YLabel.Left:
e.Graphics.FillRectangle(
new SolidBrush( LabelBackground ),
new Rectangle( 0, 0, LABEL_WIDTH, this.Height ) );
break;
case YLabel.Right:
if ( ShowList ) {
label_x = this.Width - LABEL_WIDTH - LIST_WIDTH;
} else {
label_x = this.Width - LABEL_WIDTH;
}
e.Graphics.FillRectangle(
new SolidBrush( LabelBackground ),
new Rectangle( label_x, 0, LABEL_WIDTH, this.Height ) );
break;
}
if ( YLabel != YLabel.None ) {
for ( int i = start_y; i <= end_y; i++ ) {
float sy = i * place_y;
int py = cY( sy );
e.Graphics.DrawString( sy.ToString(), font, Brushes.Black, new PointF( label_x, py ) );
}
}
//リストを描く
if ( ShowList ) {
e.Graphics.FillRectangle(
new SolidBrush( ListBackground ),
new Rectangle( this.Width - LIST_WIDTH, 0, LIST_WIDTH, this.Height ) );
e.Graphics.DrawLine(
Pens.Black,
new Point( this.Width - LIST_WIDTH, 0 ),
new Point( this.Width - LIST_WIDTH, this.Height ) );
int count = 0;
using ( Font labelfont = new Font( "Arial", 12, FontStyle.Bold, GraphicsUnit.Pixel ) ) {
foreach ( string name in m_list.Keys ) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if ( name == m_selected ) {
e.Graphics.DrawString(
name,
labelfont,
Brushes.White,
new PointF( this.Width - 40, count * 17 + 2 ) );
} else {
e.Graphics.DrawString(
name,
labelfont,
Brushes.Black,
new PointF( this.Width - 40, count * 17 + 2 ) );
}
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
e.Graphics.FillRectangle(
new SolidBrush( m_list[name].Color ),
new Rectangle( this.Width - 55, count * 17 + 2, 10, 15 ) );
count++;
}
}
}
}
m_scale_locked = false;
}
private void CurveEditor_MouseDown( object sender, MouseEventArgs e ) {
m_mouse_moved = false;
if ( e.Button == MouseButtons.Middle || (m_spacekey_down && e.Button == MouseButtons.Left ) ) {
if ( m_spacekey_down && ((e.Button & MouseButtons.Left) == MouseButtons.Left) ) {
m_drag_with_space_key = true;
}
#region MouseButtons.Middle
if ( ScrollEnabled ) {
switch ( XLabel ) {
case XLabel.Top:
if ( e.Y <= LABEL_WIDTH ) {
return;
}
break;
case XLabel.Bottom:
if ( this.Height - LABEL_WIDTH <= e.Y ) {
return;
}
break;
}
switch ( YLabel ) {
case YLabel.Left:
if ( e.X <= LABEL_WIDTH ) {
return;
}
if ( ShowList && this.Width - LIST_WIDTH <= e.X ) {
return;
}
break;
case YLabel.Right:
if ( ShowList ) {
if ( this.Width - LIST_WIDTH - LABEL_WIDTH <= e.X && e.X <= this.Width - LIST_WIDTH ) {
// 軸ラベルの部分でマウスダウン
return;
} else if ( this.Width - LIST_WIDTH <= e.X ) {
// リストの部分でマウスダウン
return;
}
} else {
if ( this.Width - LABEL_WIDTH <= e.X ) {
return;
}
}
break;
}
m_drag_start = e.Location;
m_old_xoffset = m_xoffset;
m_old_yoffset = m_yoffset;
Drag = true;
}
#endregion
} else if ( e.Button == MouseButtons.Left ) {
#region MouseButtons.Left
#region
// 右のリストの部分がクリックされたかどうかを検査
if ( ShowList && this.Width - LIST_WIDTH <= e.X ) {
int count = 0;
foreach ( string name in m_list.Keys ) {
Rectangle active = new Rectangle( this.Width - 55, count * 17, 50, 16 );
if ( active.X <= e.X && e.X <= active.X + active.Width &&
active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
m_selected = name;
if ( RescaleYEnabled ) {
/*float min, max;
m_list[m_selected].GetMinMax( out min, out max );
#if DEBUG
LipSync.Common.DebugWriteLine( "min,max=" + min + "," + max );
#endif
if ( min != max ) {
float dif = max - min;
YScale = this.GraphSize.Height / (dif * 1.1f);
float new_offset;
if ( XLabel == XLabel.Bottom ) {
new_offset = -(min - dif * 0.05f - LABEL_WIDTH / YScale);
} else {
new_offset = -min + dif * 0.05f;
}
YOffset = new_offset;
}*/
YScale = m_list_minmax[m_selected].Scale;
YOffset = m_list_minmax[m_selected].Offset;
}
this.Invalidate();
return;
}
count++;
}
m_selected = "";
this.Invalidate();
return;
}
#endregion
#region
if ( XLabel == XLabel.Top ) {
if ( 0 <= e.Y && e.Y <= LABEL_WIDTH ) {
m_change_xscale = true;
m_change_origin = e.X;
m_old_scale = XScale;
return;
}
} else if ( XLabel == XLabel.Bottom ) {
if ( this.Height - LABEL_WIDTH <= e.Y && e.Y <= this.Height ) {
m_change_xscale = true;
m_change_origin = e.X;
m_old_scale = XScale;
return;
}
}
if ( YLabel == YLabel.Left ) {
if ( 0 <= e.X && e.X <= LABEL_WIDTH ) {
m_change_yscale = true;
m_change_origin = e.Y;
m_old_scale = YScale;
return;
}
} else if ( YLabel == YLabel.Right ) {
if ( ShowList ) {
if ( this.Width - LIST_WIDTH - LABEL_WIDTH <= e.X && e.X <= this.Width - LIST_WIDTH ) {
m_change_yscale = true;
m_change_origin = e.Y;
m_old_scale = YScale;
return;
}
} else {
if ( this.Width - LABEL_WIDTH <= e.X && e.X <= this.Width ) {
m_change_yscale = true;
m_change_origin = e.Y;
m_old_scale = YScale;
return;
}
}
}
#endregion
#region
DetectSelectedPoint( e.Location, out m_picked_point_id, out m_picked_side );
#if DEBUG
Common.DebugWriteLine( "CureveEditor_MouseDown" );
Common.DebugWriteLine( " m_picked_point_id=" + m_picked_point_id );
Common.DebugWriteLine( " m_picked_side=" + m_picked_side );
#endif
if ( m_picked_point_id >= 0 ) {
if ( m_picked_side == PickedSide.Base ) {
m_before_edit = m_list[m_selected][m_picked_point_id].Base;
} else if ( m_picked_side == PickedSide.Left ) {
m_before_edit = m_list[m_selected][m_picked_point_id].ControlLeft;
} else {
m_before_edit = m_list[m_selected][m_picked_point_id].ControlRight;
}
_number_visible = true;
m_mouse_down_mode = MouseDownMode.MovePoint;
Invalidate();
} else {
m_mouse_down_mode = MouseDownMode.Nothing;
}
#endregion
#endregion
}
}
/// <summary>
/// カーブエディタ画面上の指定された点にあるデータ点を調べます。
/// </summary>
/// <param name="e"></param>
/// <param name="picked_point"></param>
/// <param name="picied_side"></param>
private void DetectSelectedPoint( Point e, out int picked_point_id, out PickedSide picked_side ) {
picked_point_id = -1;
picked_side = PickedSide.Base;
if ( m_selected != "" ) {
Rectangle active;
Point pt;
for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
pt = cPoint( m_list[m_selected].List[i].Base );
int width = m_data_point_size;
pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
if ( active.X <= e.X && e.X <= active.X + active.Width &&
active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
picked_point_id = m_list[m_selected].List[i].ID;
picked_side = PickedSide.Base;
//m_before_edit = m_list[m_selected][i].Base;
//m_mouse_upped = false;
//_number_visible = true;
return;
}
pt = cPoint( m_list[m_selected].List[i].ControlLeft );
width = m_control_point_size;
pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
if ( active.X <= e.X && e.X <= active.X + active.Width &&
active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
picked_point_id = m_list[m_selected].List[i].ID;
picked_side = PickedSide.Left;
//m_before_edit = m_list[m_selected][i].ControlLeft;
//m_mouse_upped = false;
//_number_visible = true;
return;
}
pt = cPoint( m_list[m_selected].List[i].ControlRight );
width = m_control_point_size;
pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
if ( active.X <= e.X && e.X <= active.X + active.Width &&
active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
picked_point_id = m_list[m_selected].List[i].ID;
picked_side = PickedSide.Right;
//m_before_edit = m_list[m_selected][i].ControlRight;
//m_mouse_upped = false;
//_number_visible = true;
return;
}
}
//m_picked_point = -1;
//m_mouse_upped = true;
}
}
private void CurveEditor_MouseUp( object sender, MouseEventArgs e ) {
Drag = false;
m_drag_with_space_key = false;
if ( m_selected != "" && m_mouse_down_mode == MouseDownMode.MovePoint && m_picked_point_id >= 0 && m_mouse_moved ) {
PointF new_pt = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
Register( Execute( run ) );
this.Invalidate();
}
m_mouse_down_mode = MouseDownMode.Nothing;
m_change_xscale = false;
m_change_yscale = false;
_number_visible = false;
}
private void CurveEditor_MouseMove( object sender, MouseEventArgs e ) {
m_mouse_moved = true;
_mouse_position = e.Location;
if ( Drag ) {
bool drag_ok = false;
if ( m_drag_with_space_key ) {
if ( m_spacekey_down ) {
drag_ok = true;
}
} else {
drag_ok = true;
}
if ( drag_ok && m_selected != "" ) {
int dx = e.X - m_drag_start.X;
int dy = m_drag_start.Y - e.Y;
XOffset = m_old_xoffset + dx / m_xscale;
YOffset = m_old_yoffset + dy / m_yscale;
m_list_minmax[m_selected].Offset = YOffset;
this.Invalidate();
}
} else if ( m_picked_point_id >= 0 && m_mouse_down_mode == MouseDownMode.MovePoint ) {
PointF new_pt = sPoint( e.Location );
BezierPoint bp = m_list[m_selected][m_picked_point_id];
int centre = m_list[m_selected].GetIndexFromId( m_picked_point_id );
int left_id = m_list[m_selected].GetIdFromIndex( centre - 1 );
int right_id = m_list[m_selected].GetIdFromIndex( centre + 1 );
switch ( m_picked_side ) {
case PickedSide.Base:
#region PickedSide.Base
if ( 1 <= centre ) {
if ( new_pt.X < m_list[m_selected][left_id].Base.X ) {
new_pt.X = m_list[m_selected][left_id].Base.X;
}
if ( bp.ControlLeftType != ControlType.None ) {
float x1 = m_list[m_selected][left_id].Base.X;
float x2 = m_list[m_selected][left_id].ControlRight.X;
float x3 = new_pt.X + (bp.ControlLeft.X - bp.Base.X);
float x4 = new_pt.X;
if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
bp.Base = new PointF( bp.Base.X, new_pt.Y );
_number = bp.Base;
this.Invalidate();
return;
}
}
}
if ( centre < m_list[m_selected].Count - 1 ) {
if ( m_list[m_selected][right_id].Base.X < new_pt.X ) {
new_pt.X = m_list[m_selected][right_id].Base.X;
}
if ( bp.ControlRightType != ControlType.None ) {
float x1 = new_pt.X;
float x2 = new_pt.X + (bp.ControlRight.X - bp.Base.X);
float x3 = m_list[m_selected][right_id].ControlLeft.X;
float x4 = m_list[m_selected][right_id].Base.X;
if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
bp.Base = new PointF( bp.Base.X, new_pt.Y );
_number = bp.Base;
this.Invalidate();
return;
}
}
}
bp.Base = new_pt;
_number = bp.Base;
#endregion
break;
case PickedSide.Right:
#region PickedSide.Right
if ( centre < m_list[m_selected].Count - 1 ) {
float x1 = bp.Base.X;
float x2 = new_pt.X;
float x3 = m_list[m_selected][right_id].ControlLeft.X;
float x4 = m_list[m_selected][right_id].Base.X;
bool is_right = IsBezierImplicit( x1, x2, x3, x4 );
bool is_left = true;
float dx = new_pt.X - bp.Base.X;
float dy = new_pt.Y - bp.Base.Y;
float k = 1f;
//if ( bp.ControlRightType == ControlType.Master && m_picked_point >= 1 ) {
if ( bp.ControlRightType != ControlType.None && centre >= 1 ) {
x1 = m_list[m_selected][left_id].Base.X;
x2 = m_list[m_selected][left_id].ControlRight.X;
float dx1 = (bp.ControlLeft.X - bp.Base.X) * XScale;
float dy1 = (bp.ControlLeft.Y - bp.Base.Y) * YScale;
float length = (float)Math.Sqrt( dx1 * dx1 + dy1 * dy1 );
float tdx = dx * XScale;
float tdy = dy * YScale;
k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
x3 = bp.Base.X - dx * k;
x4 = bp.Base.X;
is_left = IsBezierImplicit( x1, x2, x3, x4 );
}
if ( is_right && is_left ) {
bp.ControlRight = new_pt;
_number = bp.ControlRight;
if ( bp.ControlRightType == ControlType.Master && centre >= 1 && bp.ControlLeftType != ControlType.None ) {
bp.ControlLeft = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
}
this.Invalidate();
return;
} else {
if ( centre == 0 ) {
bp.ControlRight = new PointF( bp.ControlRight.X, new_pt.Y );
_number = bp.ControlRight;
this.Invalidate();
return;
} else {
//とりあえずnew_ptのyだけ替えてみて再評価してみる
new_pt = new PointF( bp.ControlRight.X, new_pt.Y );
dx = new_pt.X - bp.Base.X;
dy = new_pt.Y - bp.Base.Y;
x1 = bp.Base.X;
x2 = new_pt.X;
x3 = m_list[m_selected][right_id].ControlLeft.X;
x4 = m_list[m_selected][right_id].Base.X;
is_right = IsBezierImplicit( x1, x2, x3, x4 );
is_left = true;
if ( bp.ControlRightType == ControlType.Master ) {
x1 = m_list[m_selected][left_id].Base.X;
x2 = m_list[m_selected][left_id].ControlRight.X;
float dx2 = (bp.ControlLeft.X - bp.Base.X) * XScale;
float dy2 = (bp.ControlLeft.Y - bp.Base.Y) * YScale;
float length = (float)Math.Sqrt( dx2 * dx2 + dy2 * dy2 );
float tdx = dx * XScale;
float tdy = dy * YScale;
k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
x3 = bp.Base.X - dx * k;
x4 = bp.Base.X;
is_left = IsBezierImplicit( x1, x2, x3, x4 );
}
if ( is_right && is_left ) {
bp.ControlRight = new PointF( bp.ControlRight.X, new_pt.Y );
_number = bp.ControlRight;
if ( bp.ControlRightType == ControlType.Master ) {
bp.ControlLeft = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
}
this.Invalidate();
return;
}
}
}
} else {
bp.ControlRight = new_pt;
_number = bp.ControlRight;
}
#endregion
break;
case PickedSide.Left:
#region PickedSide.Left
if ( centre >= 1 ) {
float x1 = m_list[m_selected][left_id].Base.X;
float x2 = m_list[m_selected][left_id].ControlRight.X;
float x3 = new_pt.X;
float x4 = bp.Base.X;
bool is_left = IsBezierImplicit( x1, x2, x3, x4 );
bool is_right = true;
float dx = new_pt.X - bp.Base.X;
float dy = new_pt.Y - bp.Base.Y;
float k = 1f;
if ( bp.ControlLeftType != ControlType.None && centre < m_list[m_selected].Count - 1 ) {
//if ( bp.ControlLeftType == ControlType.Master && m_picked_point < m_list[m_selected].Count - 1 ) {
float dx1 = (bp.ControlRight.X - bp.Base.X) * XScale;
float dy1 = (bp.ControlRight.Y - bp.Base.Y) * YScale;
float length = (float)Math.Sqrt( dx1 * dx1 + dy1 * dy1 );
float tdx = dx * XScale;
float tdy = dy * YScale;
k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
x1 = bp.Base.X;
x2 = bp.Base.X - dx * k;
x3 = m_list[m_selected][right_id].ControlLeft.X;
x4 = m_list[m_selected][right_id].Base.X;
is_right = IsBezierImplicit( x1, x2, x3, x4 );
}
if ( is_right && is_left ) {
bp.ControlLeft = new_pt;
_number = bp.ControlLeft;
if ( bp.ControlLeftType == ControlType.Master && centre >= 1 && bp.ControlRightType != ControlType.None ) {
bp.ControlRight = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
}
this.Invalidate();
return;
} else {
if ( centre == m_list[m_selected].Count - 1 ) {
bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
_number = bp.ControlLeft;
this.Invalidate();
return;
} else {
//とりあえずnew_ptのyだけ替えてみて再評価してみる
new_pt = new PointF( bp.ControlLeft.X, new_pt.Y );
dx = new_pt.X - bp.Base.X;
dy = new_pt.Y - bp.Base.Y;
x1 = m_list[m_selected][left_id].Base.X;
x2 = m_list[m_selected][left_id].ControlRight.X;
x3 = new_pt.X;
x4 = bp.Base.X;
is_left = IsBezierImplicit( x1, x2, x3, x4 );
is_right = true;
if ( bp.ControlLeftType == ControlType.Master ) {
float dx2 = (bp.ControlRight.X - bp.Base.X) * XScale;
float dy2 = (bp.ControlRight.Y - bp.Base.Y) * YScale;
float length = (float)Math.Sqrt( dx2 * dx2 + dy2 * dy2 );
float tdx = dx * XScale;
float tdy = dy * YScale;
k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
x1 = bp.Base.X;
x2 = bp.Base.X - dx * k;
x3 = m_list[m_selected][right_id].ControlLeft.X;
x4 = m_list[m_selected][right_id].Base.X;
is_right = IsBezierImplicit( x1, x2, x3, x4 );
}
if ( is_right && is_left ) {
bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
_number = bp.ControlLeft;
if ( bp.ControlLeftType == ControlType.Master ) {
bp.ControlRight = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
}
this.Invalidate();
return;
}
}
}
} else {
bp.ControlLeft = new_pt;
_number = bp.ControlLeft;
}
//昔の=>
/*if ( m_picked_point >= 1 ) {
float x1 = m_list[m_selected][m_picked_point - 1].Base.X;
float x2 = m_list[m_selected][m_picked_point - 1].ControlRight.X;
float x3 = new_pt.X;
float x4 = bp.Base.X;
if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
this.Invalidate();
return;
}
}
bp.ControlLeft = new_pt;*/
//<=ここまで
#endregion
break;
}
this.Invalidate();
} else if ( m_change_xscale ) {
float new_scale = m_old_scale * (float)Math.Pow( 10, (e.X - m_change_origin) / CHANGE_SCALE_ORDER );
if ( new_scale < MinXScale ) {
new_scale = MinXScale;
} else if ( MaxXScale < new_scale ) {
new_scale = MaxXScale;
}
if ( new_scale != XScale ) {
XOffset = (m_change_origin) * (1f / new_scale - 1f / XScale) + XOffset;
XScale = new_scale;
this.Invalidate();
}
} else if ( m_change_yscale ) {
float new_scale = m_old_scale * (float)Math.Pow( 10, (e.Y - m_change_origin) / CHANGE_SCALE_ORDER );
if ( new_scale < MinYScale ) {
new_scale = MinYScale;
} else if ( MaxYScale < new_scale ) {
new_scale = MaxYScale;
}
if ( new_scale != YScale ) {
YOffset = m_change_origin * (1f / new_scale - 1f / YScale) + YOffset;
YScale = new_scale;
m_list_minmax[m_selected].Offset = YOffset;
m_list_minmax[m_selected].Scale = YScale;
this.Invalidate();
}
}
this.Invalidate();
}
private void CurveEditor_Resize( object sender, EventArgs e ) {
this.Invalidate();
}
private void CurveEditor_MouseDoubleClick( object sender, MouseEventArgs e ) {
if ( m_selected == "" ) {
return;
}
float x = sX( e.X );
float y = sY( e.Y );
int picked_id;
PickedSide picked_side;
DetectSelectedPoint( e.Location, out picked_id, out picked_side );
if ( picked_id >= 0 ) {
m_picked_point_id = picked_id;
m_picked_side = picked_side;
m_before_edit = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
using ( LipSync.SetSize<float> dlg = new LipSync.SetSize<float>(
_( "Numeric entry" ),
"x",
"y",
m_before_edit.X,
m_before_edit.Y ) ) {
if ( dlg.ShowDialog() == DialogResult.OK ) {
SizeF res = new SizeF( dlg.ResultWidth, dlg.ResultHeight );
PointF new_pt = new PointF( res.Width, res.Height );
Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
Register( Execute( run ) );
this.Invalidate();
}
}
} else {
float handle_length;
float slope = 0f;
if ( m_list[m_selected].Count == 0 ) {
handle_length = x * 0.5f;
} else {
int right_point = -1;
//右側の点を検索
for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
if ( x == m_list[m_selected].List[i].Base.X ) {
//xが等しくなる位置にはデータ点を追加できない仕様。
return;
}
if ( x < m_list[m_selected].List[i].Base.X ) {
right_point = i;
break;
}
}
if ( right_point == -1 ) {
// 最も右
float dx = Math.Abs( x - m_list[m_selected].List[m_list[m_selected].List.Count - 1].Base.X );
handle_length = dx / 2;
} else if ( right_point == 0 ) {
float dx = Math.Abs( m_list[m_selected].List[0].Base.X - x );
handle_length = dx / 2;
} else {
float dx_r = Math.Abs( m_list[m_selected].List[right_point].Base.X - x );
float dx_l = Math.Abs( x - m_list[m_selected].List[right_point - 1].Base.X );
handle_length = Math.Min( dx_r, dx_l ) / 2;
slope = (m_list[m_selected].List[right_point].Base.Y - m_list[m_selected].List[right_point - 1].Base.Y) /
(m_list[m_selected].List[right_point].Base.X - m_list[m_selected].List[right_point - 1].Base.X);
}
}
PointF p_left, p_right;
p_right = new PointF( x + handle_length, y + handle_length * slope );
p_left = new PointF( x - handle_length, y - handle_length * slope );
BezierPoint bp = new BezierPoint( new PointF( x, y ),
p_left,
p_right );
bp.ControlLeftType = m_last_added_type;
bp.ControlRightType = m_last_added_type;
Command run = Command.GCommandAdd( m_selected, bp );
Register( Execute( run ) );
m_picked_side = PickedSide.Base;
for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
BezierPoint bpoint = m_list[m_selected].List[i];
if ( x == bpoint.Base.X ) {
m_picked_point_id = bpoint.ID;
break;
}
}
}
this.Invalidate();
}
private void CurveEditor_PreviewKeyDown( object sender, PreviewKeyDownEventArgs e ) {
if ( m_selected != "" && m_picked_point_id >= 0 ) {
if ( m_picked_side != PickedSide.Base ) {
switch ( e.KeyCode ) {
case Keys.H:
if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.Master ) {
Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.Master );
Register( Execute( run ) );
}
break;
case Keys.V:
if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.Normal ) {
Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.Normal );
Register( Execute( run ) );
}
break;
case Keys.Delete:
if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.None ) {
Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.None );
Register( Execute( run ) );
}
break;
}
this.Invalidate();
} else {
ControlType target;
switch ( e.KeyCode ) {
case Keys.H:
target = ControlType.Master;
break;
case Keys.V:
target = ControlType.Normal;
break;
case Keys.Delete:
Command run = Command.GCommandDelete( m_selected, m_picked_point_id );
Register( Execute( run ) );
m_picked_point_id = -1;
this.Invalidate();
return;
default:
return;
}
BezierPoint bpoint = m_list[m_selected][m_picked_point_id];
if ( bpoint != null ) {
if ( m_list[m_selected][m_picked_point_id].ControlLeftType != target ||
m_list[m_selected][m_picked_point_id].ControlRightType != target ) {
BezierPoint bp = m_list[m_selected][m_picked_point_id].Clone();
bp.ControlLeftType = target;
bp.ControlRightType = target;
Command run = Command.GCommandEdit( m_selected, m_picked_point_id, bp );
Register( Execute( run ) );
this.Invalidate();
}
}
}
}
}
private Command Execute( Command run ) {
#if DEBUG
Common.DebugWriteLine( "CurveEditor.Execute" );
/*Common.DebugWriteLine( " before" );
for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
BezierPoint bp = m_list[m_selected].List[i];
Common.DebugWriteLine( " Base.X=" + bp.Base.X + ", ID=" + bp.ID );
}*/
#endif
Command ret = null;
switch ( run.Type ) {
case CommandType.Position:
switch ( run.Side ) {
case PickedSide.Base:
ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].Base );
break;
case PickedSide.Left:
ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlLeft );
break;
case PickedSide.Right:
ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlRight );
break;
}
#if DEBUG
LipSync.Common.DebugWriteLine( " before;Position=" + m_list[run.ID][run.PointID].GetPosition( PickedSide.Base ) );
#endif
m_list[run.ID][run.PointID].SetPosition( run.Side, run.Position );
#if DEBUG
for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
if ( AppManager.SaveData.m_telop_ex2[i].Text == run.ID ) {
}
}
#endif
break;
case CommandType.Type:
switch ( run.Side ) {
case PickedSide.Left:
ret = Command.GCommandChangeType( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlLeftType );
m_list[run.ID][run.PointID].ControlLeftType = run.ControlType;
break;
case PickedSide.Right:
ret = Command.GCommandChangeType( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlRightType );
m_list[run.ID][run.PointID].ControlRightType = run.ControlType;
break;
}
break;
case CommandType.Add:
BezierPoint bp = run.BezierPoint.Clone();
bp.ID = m_list[run.ID].GetNextID();
ret = Command.GCommandDelete( run.ID, bp.ID );
m_list[run.ID].Add( bp );
break;
case CommandType.Delete:
ret = Command.GCommandAdd( run.ID, m_list[run.ID][run.PointID] );
m_list[run.ID].RemoveAt( run.PointID );
break;
case CommandType.Edit:
ret = Command.GCommandEdit( run.ID, run.PointID, m_list[run.ID][run.PointID] );
m_list[run.ID][run.PointID] = run.BezierPoint.Clone();
break;
default:
return null;
}
if ( this.CurveEdited != null ) {
CurveEdited();
}
#if DEBUG
/*Common.DebugWriteLine( " after" );
for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
BezierPoint bp = m_list[m_selected].List[i];
Common.DebugWriteLine( " Base.X=" + bp.Base.X + ", ID=" + bp.ID );
}*/
#endif
return ret;
}
/// <summary>
/// アンドゥ処理行います
/// </summary>
public void Undo() {
if ( IsUndoAvailable ) {
Command run = m_commands[m_command_position].Clone();
m_commands[m_command_position] = Execute( run );
m_command_position--;
this.Invalidate();
}
}
/// <summary>
/// リドゥ処理行います
/// </summary>
public void Redo() {
if ( IsRedoAvailable ) {
Command run = m_commands[m_command_position + 1].Clone();
m_commands[m_command_position + 1] = Execute( run );
m_command_position++;
this.Invalidate();
}
}
/// <summary>
/// リドゥ操作が可能かどうかを表す値を取得します
/// </summary>
public bool IsRedoAvailable {
get {
if ( m_command_position + 1 < m_commands.Count ) {
return true;
} else {
return false;
}
}
}
/// <summary>
/// アンドゥ操作が可能かどうかを表す値を取得します
/// </summary>
public bool IsUndoAvailable {
get {
if ( 0 > m_command_position ) {
return false;
} else {
return true;
}
}
}
/// <summary>
/// コマンドバッファに指定されたコマンドを登録します
/// </summary>
/// <param name="command"></param>
void Register( Command command ) {
if ( m_command_position == m_commands.Count - 1 ) {
// 新しいコマンドバッファを追加する場合
m_commands.Add( command.Clone() );
m_command_position = m_commands.Count - 1;
} else {
// 既にあるコマンドバッファを上書きする場合
m_commands[m_command_position + 1].Dispose();
m_commands[m_command_position + 1] = command.Clone();
for ( int i = m_commands.Count - 1; i >= m_command_position + 2; i-- ) {
m_commands.RemoveAt( i );
}
m_command_position++;
}
}
private void CurveEditor_KeyDown( object sender, KeyEventArgs e ) {
if ( e.KeyCode == Keys.Space ) {
m_spacekey_down = true;
this.Cursor = HAND;
} else {
m_spacekey_down = false;
this.Cursor = Cursors.Default;
}
}
private void CurveEditor_KeyUp( object sender, KeyEventArgs e ) {
m_spacekey_down = false;
this.Cursor = Cursors.Default;
}
void _num_input_FormClosing( object sender, FormClosingEventArgs e ) {
e.Cancel = true;
}
/// <summary>
/// データ点の数値入力用のコンテキストメニューを初期化します
/// </summary>
private void InitializeContextMenu() {
if( _cmenu != null ) {
_cmenu.Dispose();
}
if( _cmenuNumericInput != null ) {
_cmenuNumericInput.Dispose();
}
_cmenu = new ContextMenuStrip();
_cmenu.ShowCheckMargin = false;
_cmenu.ShowImageMargin = false;
_cmenuNumericInput = new ToolStripMenuItem();
_cmenu.Items.AddRange( new ToolStripItem[] {
_cmenuNumericInput} );
_cmenu.Name = "cmenu";
_cmenu.Size = new System.Drawing.Size( 135, 26 );
_cmenu.Font = this.Font;
_cmenuNumericInput.Name = "cmenuNumericInput";
_cmenuNumericInput.Size = new System.Drawing.Size( 134, 22 );
_cmenuNumericInput.Text = _( "Numeric entry" ) + "(&N)";
_cmenuNumericInput.Click += new EventHandler( _cmenuNumericInput_Click );
}
void _cmenuNumericInput_Click( object sender, EventArgs e ) {
m_before_edit = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
using ( LipSync.SetSize<float> dlg = new LipSync.SetSize<float>(
_( "Numeric entry" ),
"x",
"y",
m_before_edit.X,
m_before_edit.Y ) ) {
if ( dlg.ShowDialog() == DialogResult.OK ) {
SizeF res = new SizeF( dlg.ResultWidth, dlg.ResultHeight );
PointF new_pt = new PointF( res.Width, res.Height );
Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
Register( Execute( run ) );
this.Invalidate();
}
}
}
private void CurveEditor_MouseClick( object sender, MouseEventArgs e ) {
if ( (e.Button & MouseButtons.Right) == MouseButtons.Right ) {
DetectSelectedPoint( e.Location, out m_picked_point_id, out m_picked_side );
#if DEBUG
Common.DebugWriteLine( "CureveEditor_MouseClick" );
Common.DebugWriteLine( " m_picked_point_id=" + m_picked_point_id );
Common.DebugWriteLine( " m_picked_side=" + m_picked_side );
#endif
if ( m_picked_point_id >= 0 ) {
InitializeContextMenu();
_cmenu.Show( this, e.Location );
_number_visible = false;
#if DEBUG
LipSync.Common.DebugWriteLine( "MouseClick, m_picked_point_id=" + m_picked_point_id );
#endif
}
}
}
private void CurveEditor_FontChanged( object sender, EventArgs e ) {
if ( _cmenu != null ) {
_cmenu.Font = this.Font;
}
}
}
internal class Command /*: ICloneable*/ {
string m_id;
//int m_picked_index;
PickedSide m_picked_side;
PointF m_new_position;
CommandType m_command_type;
ControlType m_control_type;
BezierPoint m_bp;
//float m_x;
int m_pid;
public override string ToString() {
return "{ID=" + ID + ", PointID=" + PointID + ", Side=" + Side + ", CommandType=" + Type + ", Position=" + Position + "}";
}
public Command Clone() {
Command result = new Command();
result.m_id = this.m_id;
//result.m_picked_index = this.m_picked_index;
result.m_new_position = this.m_new_position;
result.m_command_type = this.m_command_type;
result.m_control_type = this.m_control_type;
if ( this.m_bp != null ) {
result.m_bp = this.m_bp.Clone();
}
result.m_pid = this.m_pid;
result.m_picked_side = this.m_picked_side;
return result;
}
public void Dispose(){
m_bp = null;
}
public static Command GCommandEditPosition( string ID, int picked_id, PickedSide picked_side, PointF new_position ) {
Command ret = new Command();
ret.m_id = ID;
ret.m_pid = picked_id;
ret.m_picked_side = picked_side;
ret.m_new_position = new_position;
ret.m_command_type = CommandType.Position;
ret.m_control_type = ControlType.None;
return ret;
}
public static Command GCommandChangeType( string ID, int picked_id, PickedSide picked_side, ControlType control_type ){
Command ret = new Command();
ret.m_id = ID;
ret.m_pid = picked_id;
ret.m_picked_side = picked_side;
ret.m_command_type = CommandType.Type;
ret.m_control_type = control_type;
return ret;
}
public static Command GCommandAdd( string ID, BezierPoint point ) {
Command ret = new Command();
ret.m_id = ID;
if ( point != null ) {
ret.m_bp = (BezierPoint)point.Clone();
}
ret.m_command_type = CommandType.Add;
return ret;
}
public static Command GCommandDelete( string ID, /*float*/int pid ) {
Command ret = new Command();
ret.m_id = ID;
//this.m_x = x;
ret.m_pid = pid;
ret.m_command_type = CommandType.Delete;
return ret;
}
public static Command GCommandNothing() {
return new Command();
}
private Command() {
this.m_command_type = CommandType.None;
}
public static Command GCommandEdit( string ID, int picked_id, BezierPoint point ) {
Command ret = new Command();
ret.m_id = ID;
ret.m_pid = picked_id;
if ( point != null ) {
ret.m_bp = (BezierPoint)point.Clone();
}
ret.m_command_type = CommandType.Edit;
return ret;
}
public int PointID {
get {
return m_pid;
}
}
/*public float X {
get {
return m_x;
}
}*/
public BezierPoint BezierPoint {
get {
return m_bp;
}
}
public CommandType Type{
get{
return m_command_type;
}
}
public ControlType ControlType {
get {
return m_control_type;
}
}
public string ID{
get{
return m_id;
}
}
public PickedSide Side{
get{
return m_picked_side;
}
}
public PointF Position{
get{
return m_new_position;
}
}
}
internal enum CommandType {
Position,//単に位置を変更する
Type,//制御点のタイプを変更する
Add,
Delete,
None,
Edit,
}
public enum PickedSide {
Right,
Base,
Left,
}
public enum PointType {
Circle,
Rectangle,
}
public enum XLabel {
None,
Top,
Bottom,
}
public enum YLabel {
None,
Left,
Right,
}
[Serializable]
public class BezierChain : IDisposable, ICloneable {
private List<BezierPoint> list;
private float m_default = 0f;
private Color m_color;
public bool GetKeyMinMax( out float min, out float max ) {
if ( list.Count == 0 ) {
min = 0f;
max = 0f;
return false;
}
min = float.MaxValue;
max = float.MinValue;
for ( int i = 0; i < list.Count; i++ ) {
min = Math.Min( min, list[i].Base.X );
max = Math.Max( max, list[i].Base.X );
}
return true;
}
public int GetIndexFromId( int id ) {
for ( int i = 0; i < list.Count; i++ ) {
if ( list[i].ID == id ) {
return i;
}
}
return -1;
}
public int GetIdFromIndex( int index ) {
if ( 0 <= index && index < list.Count ) {
return list[index].ID;
}
return -1;
}
public List<BezierPoint> List {
get {
return list;
}
set {
list = value;
}
}
[OnDeserialized]
void onDeserialized( StreamingContext sc ) {
for ( int i = 0; i < list.Count; i++ ) {
list[i].ID = i;
list[i].Order = i;
}
}
public void Sort() {
//list.Sort( new BezierChainOrderIgnoaringComparator() );
list.Sort();
for ( int i = 0; i < list.Count; i++ ) {
list[i].Order = i;
}
}
public void Dispose() {
if ( list != null ) {
list.Clear();
}
}
public int GetNextID() {
int max = -1;
for ( int i = 0; i < list.Count; i++ ) {
max = Math.Max( max, list[i].ID );
}
return max + 1;
}
public void GetValueMinMax( out float min, out float max ){
//todo: ベジエが有効なときに、曲線の描く最大値、最小値も考慮
min = Default;
max = Default;
foreach ( BezierPoint bp in list ) {
min = Math.Min( min, bp.Base.Y );
max = Math.Max( max, bp.Base.Y );
}
}
public object Clone() {
BezierChain result = new BezierChain( this.m_color );
foreach ( BezierPoint bp in list ) {
result.list.Add( bp.Clone() );
}
result.m_default = this.m_default;
return result;
}
public float Default {
get {
return m_default;
}
set {
m_default = value;
}
}
public BezierChain( Color curve ) {
list = new List<BezierPoint>();
m_color = curve;
}
public BezierPoint this[int id] {
get {
for ( int i = 0; i < list.Count; i++ ) {
if ( list[i].ID == id ) {
return list[i];
}
}
return null;
}
set {
for ( int i = 0; i < list.Count; i++ ) {
if ( list[i].ID == id ) {
list[i] = value;
return;
}
}
throw new Exception( "invalid point id" );
}
}
public Color Color {
get {
return m_color;
}
set {
m_color = value;
}
}
public void Add( BezierPoint bp ) {
if ( list == null ) {
list = new List<BezierPoint>();
m_color = Color.Black;
}
#if DEBUG
Common.DebugWriteLine( "BezierChain.Add" );
Common.DebugWriteLine( " before" );
for ( int i = 0; i < list.Count; i++ ) {
Common.DebugWriteLine( " Base.X=" + list[i].Base.X + ", Order=" + list[i].Order );
}
#endif
bool found = false;
for ( int i = 0; i < list.Count - 1; i++ ) {
if ( list[i].Base.X <= bp.Base.X && bp.Base.X < list[i + 1].Base.X ) {
bp.Order = list[i].Order + 1;
for ( int j = i + 1; j < list.Count; j++ ) {
list[j].Order = list[j].Order + 1;
}
found = true;
break;
}
}
if ( !found ) {
if ( list.Count == 0 ){
bp.Order = 0;
}else{
bp.Order = list[list.Count - 1].Order + 1;
}
}
list.Add( bp );
Sort();
#if DEBUG
Common.DebugWriteLine( "BezierChain.Add" );
Common.DebugWriteLine( " after" );
for ( int i = 0; i < list.Count; i++ ) {
Common.DebugWriteLine( " Base.X=" + list[i].Base.X + ", Order=" + list[i].Order );
}
#endif
}
public void RemoveAt( int id ) {
for ( int i = 0; i < list.Count; i++ ) {
if ( list[i].ID == id ) {
list.RemoveAt( i );
Sort();
return;
}
}
}
/*public void RemoveAt( float x ) {
for ( int i = 0; i < list.Count; i++ ) {
if ( list[i].Base.X == x ) {
list.RemoveAt( i );
break;
}
}
}*/
public int Count {
get {
if ( list == null ) {
return 0;
}
return list.Count;
}
}
public float GetValue( float x ) {
for ( int i = 0; i < list.Count - 1; i++ ) {
if ( list[i].Base.X <= x && x <= list[i + 1].Base.X ) {
if ( list[i].ControlRightType == ControlType.None && list[i + 1].ControlLeftType == ControlType.None ) {
PointF p1 = list[i].Base;
PointF p2 = list[i + 1].Base;
float slope = (p2.Y - p1.Y) / (p2.X - p1.X);
return p1.Y + slope * (x - p1.X);
} else {
float x1 = list[i].Base.X;
float x2 = list[i].ControlRight.X;
float x3 = list[i + 1].ControlLeft.X;
float x4 = list[i + 1].Base.X;
float a3 = x4 - 3 * x3 + 3 * x2 - x1;
float a2 = 3 * x3 - 6 * x2 + 3 * x1;
float a1 = 3 * (x2 - x1);
float a0 = x1;
if ( x1 == x ) {
return list[i].Base.Y;
} else if ( x4 == x ) {
return list[i + 1].Base.Y;
} else {
float t = SolveCubicEquation( a3, a2, a1, a0, x );
x1 = list[i].Base.Y;
x2 = list[i].ControlRight.Y;
x3 = list[i + 1].ControlLeft.Y;
x4 = list[i + 1].Base.Y;
a3 = x4 - 3 * x3 + 3 * x2 - x1;
a2 = 3 * x3 - 6 * x2 + 3 * x1;
a1 = 3 * (x2 - x1);
a0 = x1;
return ((a3 * t + a2) * t + a1) * t + a0;
}
}
}
}
return m_default;
}
/// <summary>
/// 3次方程式a3*x^3 + a2*x^2 + a1*x + a0 = ansの解をニュートン法を使って計算します。ただし、単調増加である必要がある。
/// </summary>
/// <param name="a3"></param>
/// <param name="a2"></param>
/// <param name="a1"></param>
/// <param name="a0"></param>
/// <param name="ans"></param>
/// <param name="suggested_t"></param>
/// <returns></returns>
private static float SolveCubicEquation( float a3, float a2, float a1, float a0, float ans ) {
double EPSILON = 1e-9;
double suggested_t = 0.4;
double a3_3 = a3 * 3.0;
double a2_2 = a2 * 2.0;
while ( (a3_3 * suggested_t + a2_2) * suggested_t + a1 == 0.0 ) {
suggested_t += 0.1;
}
double x = suggested_t;
double new_x = suggested_t;
for( int i = 0; i < 5000; i++ ){
new_x = x - (((a3 * x + a2) * x + a1) * x + a0 - ans) / ((a3_3 * x + a2_2) * x + a1);
if ( Math.Abs( new_x - x ) < EPSILON * new_x ) {
break;
}
x = new_x;
}
return (float)new_x;
}
}
public enum ControlType {
None,
Normal,
Master,
}
/// <summary>
/// ベジエ曲線を構成するデータ点。
/// </summary>
[Serializable]
public class BezierPoint : IComparable<BezierPoint> {
PointF m_base;
internal PointF m_control_left;
internal PointF m_control_right;
ControlType m_type_left;
ControlType m_type_right;
[NonSerialized]
int m_id;
[OptionalField]
public int Order;
public int ID {
get {
return m_id;
}
internal set {
m_id = value;
}
}
public override string ToString() {
return "m_base=" + m_base.X + "," + m_base.Y + "\n" +
"m_control_left=" + m_control_left.X + "," + m_control_left.Y + "\n" +
"m_control_right=" + m_control_right.X + "," + m_control_right.Y + "\n" +
"m_type_left=" + m_type_left + "\n" +
"m_type_right=" + m_type_right + "\n";
}
public BezierPoint( PointF p1, PointF left, PointF right ) {
m_base = p1;
m_control_left = new PointF( left.X - m_base.X, left.Y - m_base.Y );
m_control_right = new PointF( right.X - m_base.X, right.Y - m_base.Y );
m_type_left = ControlType.None;
m_type_right = ControlType.None;
}
public BezierPoint Clone() {
BezierPoint result = new BezierPoint( this.Base, this.ControlLeft, this.ControlRight );
result.m_control_left = this.m_control_left;
result.m_control_right = this.m_control_right;
result.m_type_left = this.m_type_left;
result.m_type_right = this.m_type_right;
result.Order = this.Order;
result.m_id = this.m_id;
return result;
}
public int CompareTo( BezierPoint item ) {
if ( this.Base.X > item.Base.X ) {
return 1;
} else if ( this.Base.X < item.Base.X ) {
return -1;
} else {
return this.Order - item.Order;
/*if ( this.ID > item.ID ) {
return 1;
} else if ( this.ID < item.ID ) {
return -1;
} else {
return 0;
}*/
}
}
public PointF Base {
get {
return m_base;
}
set {
m_base = value;
}
}
public void SetPosition( PickedSide picked_side, PointF new_position ) {
if ( picked_side == PickedSide.Base ) {
this.Base = new_position;
} else if ( picked_side == PickedSide.Left ) {
this.m_control_left = new PointF( new_position.X - this.Base.X, new_position.Y - this.Base.Y);
} else {
this.m_control_right = new PointF( new_position.X - this.Base.X, new_position.Y - this.Base.Y );
}
}
public PointF GetPosition( PickedSide picked_side ) {
if ( picked_side == PickedSide.Base ) {
return this.Base;
} else if ( picked_side == PickedSide.Left ) {
return this.ControlLeft;
} else {
return this.ControlRight;
}
}
public ControlType GetControlType( PickedSide picked_side ) {
if ( picked_side == PickedSide.Left ) {
return this.ControlLeftType;
} else if ( picked_side == PickedSide.Right ) {
return this.ControlRightType;
} else {
return ControlType.None;
}
}
public PointF ControlLeft {
get {
if ( m_type_left != ControlType.None ) {
return new PointF( m_base.X + m_control_left.X, m_base.Y + m_control_left.Y );
} else {
return m_base;
}
}
set {
m_control_left = new PointF( value.X - m_base.X, value.Y - m_base.Y );
}
}
public PointF ControlRight {
get {
if ( m_type_right != ControlType.None ) {
return new PointF( m_base.X + m_control_right.X, m_base.Y + m_control_right.Y );
} else {
return m_base;
}
}
set {
m_control_right = new PointF( value.X - m_base.X, value.Y - m_base.Y );
}
}
public ControlType ControlLeftType {
get {
return m_type_left;
}
set {
m_type_left = value;
}
}
public ControlType ControlRightType {
get {
return m_type_right;
}
set {
m_type_right = value;
}
}
}
}