mirror of
https://git.femboyfinancial.jp/james/lipsync.git
synced 2025-02-18 00:19:02 -08:00
2654 lines
113 KiB
C#
2654 lines
113 KiB
C#
/*
|
||
* 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|