/****************************************************************
** Copyright (C) 2004 MvisionTech Ltd.
** Copyright (C) 2005 Roadware Inc.
**
** This file is part of the Canvas.NET data visualization framework.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
**********************************************************************/
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Diagnostics;
namespace GeomModel
{
public class RotatedRectangle
{
public System.Drawing.Drawing2D.Matrix trans;
public RectangleF rect;
public ArrayList ConvertToPoly()
{
PointF [] points = { rect.Location, new PointF(rect.Right,rect.Top),
new PointF(rect.Right,rect.Bottom),
new PointF(rect.Left,rect.Bottom) };
trans.TransformPoints(points);
ArrayList poly_points = new ArrayList(points);
PointF ps = (PointF)poly_points[0];
poly_points.Add(new PointF(ps.X,ps.Y));
return poly_points;
}
public RotatedRectangle(RectangleF r,System.Drawing.Drawing2D.Matrix t)
{
rect = r;
trans = t.Clone();
}
public RotatedRectangle(RectangleF r,float sx,float sy, float angle)
{
rect = r;
trans = new System.Drawing.Drawing2D.Matrix();
trans.Translate(sx,sy);
trans.RotateAt(angle,new PointF(sx,sy),System.Drawing.Drawing2D.MatrixOrder.Append);
}
public ArrayList IntersectionLocal(RectangleF r1,
System.Drawing.Drawing2D.Matrix t1)
{
System.Drawing.Drawing2D.Matrix t2 = t1.Clone();
System.Drawing.Drawing2D.Matrix t_inv = trans.Clone();
t_inv.Invert();
t2.Multiply(t_inv,System.Drawing.Drawing2D.MatrixOrder.Append);
PointF [] points = { r1.Location, new PointF(r1.Right,r1.Top),
new PointF(r1.Right,r1.Bottom),new PointF(r1.Left,r1.Bottom) };
t2.TransformPoints(points);
ArrayList poly_points = new ArrayList(points);
ArrayList clip = Geom.ClipPolylineOnRectangle(rect,poly_points);
return clip;
}
public ArrayList Intersection(RotatedRectangle r1)
{
return Intersection(r1.rect,r1.trans);
}
public bool IsIntersected(RotatedRectangle r1)
{
ArrayList clip = Intersection(r1);
if(clip.Count == 0)
return false;
return true;
}
public ArrayList Intersection(RectangleF r1,
System.Drawing.Drawing2D.Matrix t1)
{
ArrayList clip = IntersectionLocal(r1,t1);
ArrayList clip_t = new ArrayList();
foreach(ArrayList segment in clip)
{
PointF [] ps = new PointF [segment.Count];
segment.CopyTo(ps);
trans.TransformPoints(ps);
clip_t.Add(new ArrayList(ps));
}
return clip_t;
}
public bool IsIntersected(RectangleF r1,
System.Drawing.Drawing2D.Matrix t1)
{
ArrayList clip = IntersectionLocal(r1,t1);
if(clip.Count == 0)
return false;
return true;
}
}
public class ObjectsGrid
{
ArrayList m_objects_grid = new ArrayList();
public float m_grid_x = 0;
public float m_grid_y = 0;
public float m_grid_w = 0;
public float m_grid_h = 0;
public float m_grid_dx = 0;
public float m_grid_dy = 0;
public int m_grid_cell_div = 20;
public void Clear()
{
m_objects_grid.Clear();
}
public int Count
{
get
{
return m_objects_grid.Count;
}
}
public void draw(Graphics graphics, System.Drawing.Drawing2D.Matrix trans)
{
System.Drawing.Pen y_pen = new Pen(System.Drawing.Color.GreenYellow,1);
System.Drawing.Pen g_pen = new Pen(System.Drawing.Color.Green,1);
for(int k=0; k<m_grid_cell_div*m_grid_cell_div; k++)
{
int cnt = ((ArrayList)m_objects_grid[k]).Count;
float y1 = m_grid_y+(k/m_grid_cell_div)*m_grid_dy;
float x1 = m_grid_x+(k%m_grid_cell_div)*m_grid_dx;
PointF [] cell_points = { new PointF(x1,y1),
new PointF(x1+m_grid_dx,y1+m_grid_dy)};
trans.TransformPoints(cell_points);
System.Drawing.Pen c_pen = y_pen;
if(cnt > 0)
c_pen = g_pen;
graphics.DrawRectangle(c_pen,cell_points[0].X,
cell_points[0].Y,
cell_points[1].X-cell_points[0].X,
cell_points[1].Y-cell_points[0].Y);
}
}
/// <summary>
/// Move object to the new location.
/// </summary>
/// <param name="old_box"></param>
/// <param name="new_box"></param>
/// <param name="shape_id"></param>
/// <returns></returns>
public bool moveObject(RectangleF old_box,RectangleF new_box,
int shape_id)
{
if(m_grid_x > new_box.X ||
m_grid_y > new_box.Y ||
m_grid_x+m_grid_w < new_box.X+new_box.Width ||
m_grid_y+m_grid_h < new_box.Y+new_box.Height )
{
this.Clear();
return true;
}
// remove from old location:
int grid_x1 = (int)((old_box.X-m_grid_x)/m_grid_dx);
int grid_y1 = (int)((old_box.Y-m_grid_y)/m_grid_dy);
int grid_x2 = (int)((old_box.X+old_box.Width-m_grid_x)/m_grid_dx);
int grid_y2 = (int)((old_box.Y+old_box.Height-m_grid_y)/m_grid_dy);
grid_x2 += 1;
grid_y2 += 1;
if(grid_x1 < 0)
grid_x1 = 0;
if(grid_x2 > m_grid_cell_div)
grid_x2 = m_grid_cell_div;
if(grid_y1 < 0)
grid_y1 = 0;
if(grid_y2 > m_grid_cell_div)
grid_y2 = m_grid_cell_div;
for(int y1=grid_y1; y1<grid_y2; y1++)
for(int x1=grid_x1; x1<grid_x2; x1++)
{
int it = y1*m_grid_cell_div+x1;
if(it >= m_objects_grid.Count)
continue;
ArrayList obj_list = (ArrayList)m_objects_grid[it];
int indx = obj_list.IndexOf(shape_id);
if(indx > 0)
obj_list.RemoveAt(indx);
}
grid_x1 = (int)((new_box.X-m_grid_x)/m_grid_dx);
grid_y1 = (int)((new_box.Y-m_grid_y)/m_grid_dy);
grid_x2 = (int)((new_box.X+new_box.Width-m_grid_x)/m_grid_dx);
grid_y2 = (int)((new_box.Y+new_box.Height-m_grid_y)/m_grid_dy);
grid_x2 += 1;
grid_y2 += 1;
if(grid_x1 < 0)
grid_x1 = 0;
if(grid_x2 > m_grid_cell_div)
grid_x2 = m_grid_cell_div;
if(grid_y1 < 0)
grid_y1 = 0;
if(grid_y2 > m_grid_cell_div)
grid_y2 = m_grid_cell_div;
for(int y1=grid_y1; y1<grid_y2; y1++)
for(int x1=grid_x1; x1<grid_x2; x1++)
{
int it = y1*m_grid_cell_div+x1;
if(it>=m_objects_grid.Count)
continue;
ArrayList obj_list = (ArrayList)m_objects_grid[it];
if(obj_list.IndexOf(shape_id) < 0)
obj_list.Add(shape_id);
}
return true;
}
public SortedList getObjects(RectangleF brect)
{
SortedList sorted_list = new SortedList();
int grid_x1 = (int)((brect.X-m_grid_x)/m_grid_dx);
int grid_y1 = (int)((brect.Y-m_grid_y)/m_grid_dy);
int grid_x2 = (int)((brect.Right-m_grid_x)/m_grid_dx);
int grid_y2 = (int)((brect.Bottom-m_grid_y)/m_grid_dy);
grid_x2 += 1;
grid_y2 += 1;
if(grid_x1 < 0)
grid_x1 = 0;
if(grid_x2 > m_grid_cell_div)
grid_x2 = m_grid_cell_div;
if(grid_y1 < 0)
grid_y1 = 0;
if(grid_y2 > m_grid_cell_div)
grid_y2 = m_grid_cell_div;
for(int y1=grid_y1; y1<grid_y2; y1++)
for(int x1=grid_x1; x1<grid_x2; x1++)
{
ArrayList obj_list = (ArrayList)m_objects_grid[y1*m_grid_cell_div+x1];
foreach(int item in obj_list)
{
if(!sorted_list.Contains(item))
sorted_list.Add(item,item);
}
}
return sorted_list;
}
public RectangleF BoundingBox()
{
return new RectangleF(m_grid_x,m_grid_y,
m_grid_w,m_grid_h);
}
public bool create(SortedList canvas_objects,CanvasLayer lay)
{
m_objects_grid.Clear();
int obj_count = 0;
foreach(int indx in canvas_objects.Keys)
{
CanvasItem item = (CanvasItem)(canvas_objects[indx]);
if(item.layer == lay)
obj_count++;
}
if(obj_count > 100)
m_grid_cell_div = 100;
else
m_grid_cell_div = obj_count;
if(canvas_objects.Count == 0)
return false;
for(int k=0; k<m_grid_cell_div*(m_grid_cell_div+1)+1; k++)
{
m_objects_grid.Insert(k,new ArrayList());
}
//RectangleF bbox = calcBoundingBox();
//m_grid_x = ((CanvasItem)canvas_objects[0]).X();
//m_grid_y = ((CanvasItem)canvas_objects[0]).Y();
m_grid_x = m_grid_w = 0;
m_grid_y = m_grid_h = 0;
float max_x = m_grid_x;
float max_y = m_grid_y;
bool first_item = true;
// get items bounded box:
foreach(int indx in canvas_objects.Keys)
{
CanvasItem item = (CanvasItem)(canvas_objects[indx]);
if(item.layer != lay)
continue;
RectangleF item_bbox = item.boundingBox();
if(first_item)
{
m_grid_x = item_bbox.Left;
m_grid_y = item_bbox.Top;
max_x = item_bbox.Right;
max_y = item_bbox.Bottom;
first_item = false;
continue;
}
if(m_grid_x > item_bbox.Left)
m_grid_x = item_bbox.Left;
else if(max_x < item_bbox.Right)
max_x = item_bbox.Right;
if(m_grid_y > item_bbox.Top)
m_grid_y = item_bbox.Top;
else if(max_y < item_bbox.Bottom)
max_y = item_bbox.Bottom;
}
m_grid_w = max_x - m_grid_x;
m_grid_h = max_y - m_grid_y;
if(m_grid_w == 0)
m_grid_w = 1;
if(m_grid_h == 0)
m_grid_h = 1;
m_grid_dx = m_grid_w/m_grid_cell_div;
m_grid_dy = m_grid_h/m_grid_cell_div;
foreach(int indx in canvas_objects.Keys)
{
CanvasItem item = (CanvasItem)(canvas_objects[indx]);
if(item.layer != lay)
continue;
RectangleF item_bbox = item.boundingBox();
int x_indx_1 = (int)((item_bbox.X-m_grid_x)/m_grid_dx);
int y_indx_1 = (int)((item_bbox.Y-m_grid_y)/m_grid_dy);
int x_indx_2 = (int)((item_bbox.X+item_bbox.Width-m_grid_x)/m_grid_dx);
int y_indx_2 = (int)((item_bbox.Y+item_bbox.Height-m_grid_y)/m_grid_dy);
for(int y_c=y_indx_1; y_c<=y_indx_2; y_c++)
for(int x_c=x_indx_1; x_c<=x_indx_2; x_c++)
{
int grid_indx = y_c*m_grid_cell_div+x_c;
if(grid_indx >= m_objects_grid.Count || grid_indx<0)
continue;
ArrayList obj_list = (ArrayList)m_objects_grid[grid_indx];
obj_list.Add(indx);
}
}
return true;
}
}
public class CanvasLayer
{
SortedList m_string_list = new SortedList();
SortedList m_id_to_string_map = new SortedList();
int curr_string_id;
Color m_label_color = Color.Black;
Color m_label_color_bg = Color.WhiteSmoke;
bool m_fill_label_bg = true;
public Color LabelColorBg
{
get
{
return m_label_color_bg;
}
set
{
m_label_color_bg = value;
}
}
public Color LabelColor
{
get
{
return m_label_color;
}
set
{
m_label_color = value;
}
}
public bool FillLabelBg
{
get
{
return m_fill_label_bg;
}
set
{
m_fill_label_bg = value;
}
}
public string ShapeGetLabel(int shape_id)
{
if(!m_id_to_string_map.ContainsKey(shape_id))
return "none";
return (string)m_string_list[(int)m_id_to_string_map[shape_id]];
}
public bool ShapeHasLabel(int shape_id)
{
return m_id_to_string_map.ContainsKey(shape_id);
}
public bool ShapeDeleteLabel(int shape_id)
{
if(!m_id_to_string_map.ContainsKey(shape_id))
return false;
int sh_key = (int)m_id_to_string_map[shape_id];
m_string_list.RemoveAt(m_string_list.IndexOfKey(sh_key));
return true;
}
public bool ShapeAddLabel(int shape_id,string label)
{
int str_indx = m_string_list.IndexOfValue(label);
int string_id = curr_string_id;
if(str_indx >= 0)
{
string_id = (int)m_string_list.GetKey(str_indx);
}
else
{
string_id = curr_string_id++;
m_string_list.Add(string_id,label);
}
if(m_id_to_string_map.ContainsKey(shape_id))
{
m_id_to_string_map[shape_id] = string_id;
}
else
{
m_id_to_string_map.Add(shape_id,string_id);
}
return true;
}
public Color FillColor
{
get
{
return bg_color;
}
set
{
bg_color = value;
}
}
public bool Fill
{
get
{
return m_fill;
}
set
{
m_fill = value;
}
}
public Color Color
{
get
{
return color;
}
set
{
color = value;
}
}
bool m_fill = false;
Color color = Color.Purple;
Color bg_color = Color.GreenYellow;
ObjectsGrid m_objects_grid;
double m_max_scale = -1;
public ObjectsGrid Grid
{
get
{
return m_objects_grid;
}
}
public double MinScale
{
get
{
return visiableOnScale;
}
set
{
visiableOnScale = value;
}
}
public double MaxScale
{
get
{
return m_max_scale;
}
set
{
m_max_scale = value;
}
}
double visiableOnScale = 0;
public int Order = 0;
public float LineWidth
{
get
{
return (float)line_width;
}
set
{
line_width = value;
}
}
double line_width = 2;
public bool Visiable
{
get
{
return visiable;
}
set
{
visiable = value;
}
}
bool visiable = true;
private ArrayList itemsList;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
string name;
public CanvasLayer(string n,Color col,int z,bool v)
{
name = n;
color = col;
visiableOnScale = z;
visiable = v;
itemsList = new ArrayList();
m_objects_grid = new ObjectsGrid();
}
public void add(CanvasItem i)
{
itemsList.Add(i);
}
public ArrayList getItems()
{
return itemsList;
}
}
/// <summary>
/// 2D geometry functions used in Canvas.
/// </summary>
public class Geom
{
public static ArrayList ClipSegmentOnRectangle(PointF p0,PointF p1,RectangleF r)
{
ArrayList al = new ArrayList();
PointF p_1 = new PointF();
PointF p_2 = new PointF();
try
{
if(!Geometry.Clip.SegmentOnRectangle(ref p0,ref p1,ref r,ref p_1,ref p_2))
return new ArrayList();
al.Add(p_1);
al.Add(p_2);
}
catch
{
return new ArrayList();
}
/*
// check left facet:
ArrayList al = new ArrayList();
float dx = p0.X - p1.X;
float dy = p0.Y - p1.Y;
if(dx==0 && dy==0)
return new ArrayList();
float left = (p0.X < p1.X) ? p0.X : p1.X;
bool left_intersect = false;
if((p0.X < r.Left && p1.X > r.Left) || (p0.X > r.Left && p1.X < r.Left))
{
left_intersect = true;
left = r.Left;
}
// check top facet:
bool top_intersect = false;
float top = (p0.Y < p1.Y) ? p0.Y : p1.Y;
if((p0.Y < r.Top && p1.Y > r.Top) || (p0.Y > r.Top && p1.Y < r.Top))
{
top_intersect = true;
top = r.Top;
}
// check right facet:
bool right_intersect = false;
float right = (p0.X < p1.X) ? p0.X : p1.X;
if((p0.X < r.Right && p1.X > r.Right) || (p0.X > r.Right && p1.X < r.Right))
{
right_intersect = true;
right = r.Right;
}
// check bottom
bool bottom_intersect = false;
float bottom = (p0.Y < p1.Y) ? p0.Y : p1.Y;
if((p0.Y < r.Bottom && p1.Y > r.Bottom) || (p0.Y > r.Bottom && p1.X < r.Bottom))
{
bottom_intersect = true;
bottom = r.Bottom;
}
// check intersection states:
if(left_intersect)
{
al.Add(new PointF(left,(left-p0.X)*dy/dx));
if(top_intersect)
{
al.Add(new PointF((top-p0.Y)*dx/dy,top));
return al;
}
else if(bottom_intersect)
{
al.Add(new PointF((p0.Y-bottom)*dx/dy,bottom));
return al;
}
else if(right_intersect)
{
al.Add(new PointF(right,(p0.Y-right)*dx/dy));
return al;
}
else
{
if(r.Contains(p0))
al.Add(p0);
else if(r.Contains(p1))
al.Add(p1);
else
al.Clear();
return al;
}
}
else if(top_intersect)
{
al.Add(new PointF((p0.Y-top)*dx/dy,top));
if(bottom_intersect)
{
al.Add(new PointF((p0.Y-bottom)*dx/dy,bottom));
return al;
}
else if(right_intersect)
{
al.Add(new PointF(right,(p0.Y-right)*dx/dy));
return al;
}
else
{
if(r.Contains(p0))
al.Add(p0);
else if(r.Contains(p1))
al.Add(p1);
else
al.Clear();
return al;
}
}
else if(right_intersect)
{
al.Add(new PointF(right,(p0.Y-right)*dx/dy));
if(bottom_intersect)
{
al.Add(new PointF((p0.Y-bottom)*dx/dy,bottom));
return al;
}
else
{
if(r.Contains(p0))
al.Add(p0);
else if(r.Contains(p1))
al.Add(p1);
else
al.Clear();
return al;
}
}
if(r.Contains(p0))
al.Add(p0);
else if(r.Contains(p1))
al.Add(p1);
else
al.Clear();
*/
return al;
}
public static ArrayList ClipPolylineOnRectangle(RectangleF r,ArrayList m_points)
{
// return empty array if there are no points:
if(m_points.Count == 0)
return new ArrayList();
ArrayList segments = new ArrayList();
bool prev_out = false;
PointF prev_point = (PointF)m_points[0];
ArrayList al = new ArrayList();
for(int i=0; i<m_points.Count; i++)
{
PointF p = (PointF)m_points[i];
if(prev_out || !r.Contains(p) && i > 0 )
{
ArrayList cliped_line = ClipSegmentOnRectangle(prev_point,p,r);
if(cliped_line.Count > 0)
al.AddRange(cliped_line);
}
if(r.Contains(p))
{
if(!prev_out)
al.Add(p);
prev_out = false;
}
else
{
if(al.Count > 0)
{
segments.Add(al);
al = new ArrayList();
}
prev_out = true;
}
prev_point = p;
}
// save last segment:
if(al.Count > 0)
{
segments.Add(al);
}
return segments;
}
public static RectangleF BoundingBox(ArrayList points)
{
RectangleF brect = new RectangleF(0,0,0,0);
if(points.Count == 0)
return brect;
float min_x = ((PointF)points[0]).X;
float min_y = ((PointF)points[0]).Y;
float max_x = min_x;
float max_y = min_y;
foreach(PointF p in points)
{
if(p.X < min_x)
min_x = p.X;
else if(p.X > max_x)
max_x = p.X;
if(p.Y < min_y)
min_y = p.Y;
else if(p.Y > max_y)
max_y = p.Y;
}
brect.X = min_x;
brect.Y = min_y;
brect.Width = max_x - min_x;
brect.Height = max_y - min_y;
return brect;
}
public static RectangleF BoundingBox(PointF [] points)
{
RectangleF brect = new RectangleF(0,0,0,0);
if(points.Length == 0)
return brect;
float min_x = ((PointF)points[0]).X;
float min_y = ((PointF)points[0]).Y;
float max_x = min_x;
float max_y = min_y;
foreach(PointF p in points)
{
if(p.X < min_x)
min_x = p.X;
else if(p.X > max_x)
max_x = p.X;
if(p.Y < min_y)
min_y = p.Y;
else if(p.Y > max_y)
max_y = p.Y;
}
brect.X = min_x;
brect.Y = min_y;
brect.Width = max_x - min_x;
brect.Height = max_y - min_y;
return brect;
}
public static bool pointInRect(PointF p,RectangleF rect)
{
return (p.X > rect.X) && (p.X < rect.X + rect.Width)
&& (p.Y > rect.Y) && (p.Y < rect.Y + rect.Height);
}
/// <summary>
/// calculate area of triangle defined by 3 points
/// </summary>
public static double triArea2(PointF p0,PointF p1,PointF p2)
{
return (p0.Y - p2.Y) * (p1.X - p2.X) - (p1.Y - p2.Y) * (p0.X - p2.X);
}
/// <summary>
/// calculate square distance between 2 poins
/// </summary>
public static double sqrDistance(PointF p1,PointF p2)
{
double dx = p1.X-p2.X;
double dy = p1.Y-p2.Y;
return dx*dx+dy*dy;
}
/// <summary>
/// calculate square distance from point p to segment [s0,s1]
/// </summary>
public static double segmentToPointSqrDist(PointF s0, PointF s1,PointF p)
{
double s1p_sqr = sqrDistance(s1, p );
double ps0_sqr = sqrDistance(p , s0);
double s0s1_sqr = sqrDistance(s0, s1);
if (s1p_sqr < s0s1_sqr)
{
if(s0s1_sqr < ps0_sqr)
{
// ps0_sqr s0s1_sqr s1p_sqr
if (s0s1_sqr + s1p_sqr <= ps0_sqr)
return s1p_sqr;
}
// s0s1_sqr (s1p_sqr, ps0_sqr)
if (s0s1_sqr <= s1p_sqr + ps0_sqr)
return Math.Min(s1p_sqr, ps0_sqr);
}
else
{
// s1p_sqr s0s1_sqr
if (s1p_sqr < ps0_sqr)
{
// ps0_sqr s1p_sqr s0s1_sqr
if (s1p_sqr + s0s1_sqr <= ps0_sqr)
return s1p_sqr;
}
else
{
// s1p_sqr (s0s1_sqr, ps0_sqr)
if (s0s1_sqr + ps0_sqr <= s1p_sqr)
return ps0_sqr;
}
}
double tri_area = triArea2(s0, s1, p);
return tri_area*tri_area/s0s1_sqr;
}
}
/// <summary>
/// Canvas item base class specifies canvas item common behavior
/// </summary>
public abstract class CanvasItem
{
//private Color color = Color.Purple;
public CanvasLayer layer = null;
public virtual ArrayList getBoundedPoints(RectangleF r)
{
ArrayList al = new ArrayList();
ArrayList segment = new ArrayList();
PointF p = new PointF(this.X(),this.Y());
if(r.Contains(p))
{
segment.Add(p);
}
al.Add(segment);
return al;
}
public virtual void rotate(double angle)
{
}
public virtual void resize(RectangleF r)
{
}
public virtual bool createFromLine(PointF p1,PointF p2)
{
return true;
}
public virtual bool createFromPolyline(ArrayList pl)
{
return true;
}
public virtual bool createFromRectangle(RectangleF r)
{
return true;
}
/// <summary>
/// calculate shape bounding box.
/// </summary>
/// <returns> bounding box rectangle </returns>
public abstract RectangleF boundingBox();
/// <summary>
/// get start point X
/// </summary>
/// <returns> X </returns>
public abstract float X();
/// <summary>
/// get start point Y
/// </summary>
/// <returns> Y </returns>
public abstract float Y();
/// <summary>
/// move shape to the point p.
/// </summary>
/// <param name="p"> destination point </param>
public abstract void move(PointF p);
/// <summary>
/// move shape by delta X,Y
/// </summary>
/// <param name="x"> X shift </param>
/// <param name="y"> Y shift </param>
public abstract void moveBy(float x,float y);
/// <summary>
/// draw shape on graphic device.
/// Shape stored in "real world" coordinates, converted
/// to client (graphic window) coordinate system and drawn
/// on graphic device
/// </summary>
/// <param name="graph"> graphic device </param>
/// <param name="trans"> real to client transformation </param>
public abstract void draw(Graphics graph,System.Drawing.Drawing2D.Matrix trans);
/// <summary>
/// Returns true if point close to the shape by specified distance
/// </summary>
/// <param name="pnt"> source point </param>
/// <param name="dist"> distance between shape and point </param>
/// <returns></returns>
public abstract bool isCloseToPoint(PointF pnt,float dist);
/// <summary>
/// Returns true if the shape is selected
/// </summary>
/// <returns> true or false </returns>
public abstract bool isSelected();
/// <summary>
/// Select the shape if parameter is true. Deselect otherwice.
/// </summary>
/// <param name="m"> selection flag </param>
public abstract void select(bool m);
}
public class ModelChangedEventArgs : EventArgs
{
public RectangleF rect;
public ModelChangedEventArgs(RectangleF r)
{
rect = r;
}
}
public class LayerEventArgs : EventArgs
{
public CanvasLayer lay;
public LayerEventArgs(CanvasLayer l)
{
lay = l;
}
}
public class MesureEventArgs : EventArgs
{
public PointF p1;
public PointF p2;
public MesureEventArgs(PointF p_1,PointF p_2)
{
p1 = p_1;
p2 = p_2;
}
}
public class ItemEventArgs : EventArgs
{
public int indx;
public ItemEventArgs(int i)
{
indx = i;
}
}
public class CoordEventArgs : EventArgs
{
public float X;
public float Y;
public int R;
public int G;
public int B;
public CoordEventArgs(float x,float y,int r,int g,int b)
{
X=x;
Y=y;
R=r;
G=g;
B=b;
}
}
public class ViewEventArgs : EventArgs
{
public object m_view;
public RectangleF m_rect;
public ViewEventArgs(object v,RectangleF r)
{
m_view = v;
m_rect = r;
}
}
public class CanvasGeomModel
{
private SortedList m_canvas_objects = new SortedList();
private int m_curr_item_id = 0;
private Image picture = null;
//private ArrayList m_objects_grid;
//private ObjectsGrid m_objects_grid;
public ArrayList Objects
{
get
{
return new ArrayList(m_canvas_objects.Keys);
}
}
public delegate void ItemChangedEventHandler(object sender, ItemEventArgs e);
public delegate void ModelChangedEventHandler(object sender, ModelChangedEventArgs e);
public event ItemChangedEventHandler ItemChangedEvent;
public event ModelChangedEventHandler ModelChangedEvent;
public delegate void ViewChangeEventHandler(object sender, ViewEventArgs e);
public event ViewChangeEventHandler ViewChangeEvent;
public delegate void LayerChangedEventHandler(object sender,LayerEventArgs e);
public event LayerChangedEventHandler LayerChangedEvent;
bool m_show_grid = false;
ArrayList m_layers;
bool m_debug = false;
public bool Debug
{
get
{
return m_debug;
}
set
{
m_debug = value;
}
}
//System.Diagnostics.PerformanceCounter cdCounter1;
//System.Diagnostics.PerformanceCounter cdCounter2;
private Color m_bgColor = Color.Gray;
public Color BgColor
{
set
{
m_bgColor = value;
}
get
{
return m_bgColor;
}
}
public void viewChanged(object view,RectangleF rect)
{
if(ViewChangeEvent != null)
ViewChangeEvent(this,new ViewEventArgs(view,rect));
}
public CanvasLayer layerByName(string name)
{
foreach (CanvasLayer item in m_layers)
{
if( item.Name == name)
return item;
}
return null;
}
public bool moveLayerUp(string lname)
{
SortedList sl = new SortedList();
int s_order = -1;
foreach(CanvasLayer lay in m_layers)
{
sl.Add(lay.Order,lay);
if(lname == lay.Name)
{
s_order = lay.Order;
}
}
if(s_order == -1)
return false;
int indx = sl.IndexOfKey(s_order);
if(indx<sl.Count-1)
{
CanvasLayer pl = (CanvasLayer)sl.GetByIndex(indx);
CanvasLayer nl = (CanvasLayer)sl.GetByIndex(indx+1);
pl.Order = nl.Order;
nl.Order = s_order;
if(LayerChangedEvent != null)
LayerChangedEvent(this,new LayerEventArgs(nl));
}
return true;
}
public bool moveLayerDown(string lname)
{
SortedList sl = new SortedList();
int s_order = -1;
foreach(CanvasLayer lay in m_layers)
{
sl.Add(lay.Order,lay);
if(lname == lay.Name)
{
s_order = lay.Order;
}
}
if(s_order == -1)
return false;
int indx = sl.IndexOfKey(s_order);
if(indx>0)
{
CanvasLayer pl = (CanvasLayer)sl.GetByIndex(indx-1);
CanvasLayer nl = (CanvasLayer)sl.GetByIndex(indx);
nl.Order = pl.Order;
pl.Order = s_order;
if(LayerChangedEvent != null)
LayerChangedEvent(this,new LayerEventArgs(pl));
}
return true;
}
public CanvasLayer createLayer(string name,Color color,int zoom,bool vis)
{
if( layerByName(name) != null)
return null;
CanvasLayer lay = new CanvasLayer(name,color,zoom,vis);
lay.Order = m_layers.Count;
m_layers.Add(lay);
if(LayerChangedEvent != null)
LayerChangedEvent(this,new LayerEventArgs(lay));
return lay;
}
public void showGrid(bool m)
{
m_show_grid = m;
}
public ArrayList getLayers()
{
return m_layers;
}
public CanvasGeomModel()
{
//m_objects_grid = new ArrayList();
//m_objects_grid = new ObjectsGrid();
m_layers = new ArrayList();
}
public int Add(CanvasItem item)
{
m_canvas_objects.Add(m_curr_item_id,item);
CanvasLayer lay = layerByName(item.layer.Name);
// add new layer
if( lay == null)
{
item.layer.Order = m_layers.Count;
m_layers.Add(item.layer);
if(LayerChangedEvent != null)
LayerChangedEvent(this,new LayerEventArgs(item.layer));
}
item.layer.Grid.Clear();
//m_objects_grid.Clear();
if(ItemChangedEvent != null)
ItemChangedEvent(this,new ItemEventArgs(m_curr_item_id));
return m_curr_item_id++;
}
public bool getIntersected(ArrayList list)
{
return true;
}
public RectangleF BoundingBox()
{
ArrayList points = new ArrayList();
foreach(CanvasLayer lay in m_layers)
{
RectangleF r = BoundingBox(lay);
points.Add(new PointF(r.Left,r.Top));
points.Add(new PointF(r.Right,r.Bottom));
}
return Geom.BoundingBox(points);
}
public RectangleF BoundingBox(CanvasLayer lay)
{
if(!m_layers.Contains(lay))
return new RectangleF(0,0,0,0);
if(lay.Grid.Count == 0 && m_canvas_objects.Count != 0 )
lay.Grid.create(m_canvas_objects,lay);
return new RectangleF(lay.Grid.m_grid_x,
lay.Grid.m_grid_y,
lay.Grid.m_grid_w,
lay.Grid.m_grid_h);
}
public CanvasItem GetItem(int indx)
{
return (CanvasItem)m_canvas_objects[indx];
}
public int [] GetSelected()
{
ArrayList selected_list = new ArrayList();
for(int indx=0; indx < m_canvas_objects.Count; indx++)
{
CanvasItem item = (CanvasItem)(m_canvas_objects.GetByIndex(indx));
if(item.isSelected())
{
selected_list.Add(indx);
}
}
int [] selection = new int[selected_list.Count];
selected_list.CopyTo(0,selection,0,selection.Length);
return selection;
}
public void MoveItem(int shape_id,PointF p)
{
if(!m_canvas_objects.ContainsKey(shape_id))
{
return;
}
CanvasItem ci = (CanvasItem)m_canvas_objects[shape_id];
ArrayList points = new ArrayList();
RectangleF r1 = ci.boundingBox();
points.Add(new PointF(r1.Left,r1.Top));
points.Add(new PointF(r1.Right,r1.Bottom));
points.Add(new PointF(p.X,p.Y));
points.Add(new PointF(p.X+r1.Width,p.Y+r1.Height));
// move an object in the objects grid:
RectangleF r2 = new RectangleF(p,r1.Size);
ci.layer.Grid.moveObject(r1,r2,shape_id);
ci.move(p);
RectangleF bbox = Geom.BoundingBox(points);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(bbox));
}
public void DeleteItem(int shape_id)
{
if(!m_canvas_objects.ContainsKey(shape_id))
{
return;
}
//m_objects_grid.Clear();
CanvasItem canv_item = (CanvasItem)m_canvas_objects[shape_id];
if(canv_item == null)
return;
canv_item.layer.Grid.Clear();
RectangleF bbox = canv_item.boundingBox();
CanvasLayer lay = canv_item.layer;
if(lay.getItems().Contains(canv_item))
lay.getItems().Remove(canv_item);
m_canvas_objects.RemoveAt(m_canvas_objects.IndexOfKey(shape_id));
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(bbox));
}
public void DeleteSelected()
{
//m_objects_grid.Clear();
bool not_passed = true;
ArrayList points = new ArrayList();
while(not_passed)
{
not_passed = false;
for(int indx=0; indx < m_canvas_objects.Count; indx++)
{
CanvasItem item = (CanvasItem)(m_canvas_objects.GetByIndex(indx));
if(item.isSelected())
{
RectangleF r1 = item.boundingBox();
points.Add(new PointF(r1.Left,r1.Top));
points.Add(new PointF(r1.Right,r1.Bottom));
m_canvas_objects.RemoveAt(indx);
item.layer.Grid.Clear();
not_passed = true;
break;
}
}
}
RectangleF bbox = Geom.BoundingBox(points);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(bbox));
}
public void moveBySelected(float dx,float dy)
{
// move selected objects
ArrayList points = new ArrayList();
for(int indx=0; indx < m_canvas_objects.Count; indx++)
{
CanvasItem item = (CanvasItem)(m_canvas_objects.GetByIndex(indx));
if(item.isSelected())
{
RectangleF r1 = item.boundingBox();
points.Add(new PointF(r1.Left,r1.Top));
points.Add(new PointF(r1.Right,r1.Bottom));
points.Add(new PointF(r1.Left+dx,r1.Top+dy));
points.Add(new PointF(r1.Right+dx,r1.Bottom+dy));
RectangleF r2 = new RectangleF(r1.Left+dx,r1.Top+dy,
r1.Width,r1.Height);
item.layer.Grid.moveObject(r1,r2,indx);
item.moveBy(dx,dy);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(item.boundingBox()));
}
}
RectangleF bbox = Geom.BoundingBox(points);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(bbox));
}
public CanvasItem GetByIndex(int indx)
{
return (CanvasItem)(m_canvas_objects.GetByIndex(indx));
}
public int Count
{
get
{
return m_canvas_objects.Count;
}
}
public int GetKey(int indx)
{
return (int)m_canvas_objects.GetKey(indx);
}
public CanvasItem GetByKey(int key)
{
return (CanvasItem)m_canvas_objects[key];
}
public void draw(Graphics graphics,System.Drawing.Drawing2D.Matrix trans,bool draw_labels)
{
RectangleF rect = graphics.ClipBounds;
rect.X = rect.X-5;
rect.Y =rect.Y-5;
rect.Width+=10;
rect.Height+=10;
if(rect.Width == 0 || rect.Height == 0)
return;
string d_msg = "draw rect "+rect.Size.ToString();
Trace.WriteLine(d_msg);
long start_draw_t = DateTime.Now.Ticks;
Bitmap bmp = new Bitmap((int)rect.Width,(int)rect.Height);
Graphics temp_graph = Graphics.FromImage(bmp);
//SolidBrush brush = new SolidBrush(Color.Gray);
SolidBrush brush = new SolidBrush(m_bgColor);
temp_graph.FillRectangle(brush,0,0,rect.Width,rect.Height);
System.Drawing.Drawing2D.Matrix inv_trans = trans.Clone();
inv_trans.Invert();
PointF [] src_pnts = {new PointF(rect.X,rect.Y),
new PointF(rect.X+rect.Width,rect.Y+rect.Height)};
inv_trans.TransformPoints(src_pnts);
RectangleF bmp_rect = new RectangleF(0,0,rect.Width,rect.Height);
/*
RectangleF src_rect = new RectangleF(src_pnts[0].X,
src_pnts[0].Y,
src_pnts[1].X-src_pnts[0].X,
src_pnts[1].Y-src_pnts[0].Y);
*/
RectangleF src_rect = Geom.BoundingBox(src_pnts);
//RectangleF rect = new RectangleF(0,0,this.Width+1,this.Height+1);
//graphics.Transform = m_trans;
if(this.picture != null)
{
temp_graph.DrawImage(this.picture, bmp_rect,src_rect,GraphicsUnit.Pixel);
}
graphics.DrawImage(bmp,rect);
bmp.Dispose();
temp_graph.Dispose();
brush.Dispose();
SortedList lay_list = new SortedList();
// sort layers list
foreach(CanvasLayer lay in m_layers)
{
lay_list.Add(lay.Order,lay);
}
foreach(int key in lay_list.Keys)
{
SortedList labels_list = new SortedList();
CanvasLayer curr_lay = (CanvasLayer)lay_list[key];
if(!curr_lay.Visiable)
continue;
float scale = Math.Abs(trans.Elements[0]);
if(scale < curr_lay.MinScale)
continue;
if(curr_lay.MaxScale > 0 && scale > curr_lay.MaxScale)
continue;
if(curr_lay.Grid.Count == 0)
curr_lay.Grid.create(m_canvas_objects,curr_lay);
if(m_show_grid)
{
curr_lay.Grid.draw(graphics,trans);
}
// display objects in overlapped by the view grid cells
SortedList sorted_list = curr_lay.Grid.getObjects(src_rect);
string msg = "display " + sorted_list.Count.ToString() + " objects";
Trace.Indent();
Trace.WriteLine(msg);
long start_t = DateTime.Now.Ticks;
//Console.WriteLine( "\t-KEY-\t-VALUE-" );
RectangleF client_rect = new RectangleF();
int n=0;
foreach(int k in sorted_list.Keys)
{
if(!m_canvas_objects.ContainsKey(k))
continue;
int shape_id = k;
CanvasItem item = (CanvasItem)(m_canvas_objects[shape_id]);
if(item == null)
continue;
if(draw_labels && curr_lay.ShapeHasLabel(shape_id))
{
ArrayList segments = item.getBoundedPoints(Geom.BoundingBox(src_pnts));
if(segments.Count>0)
labels_list.Add(shape_id,segments);
}
item.draw(graphics,trans);
if(n++>5000)
break;
}
long end_t = DateTime.Now.Ticks;
double diff = (end_t - start_t)/10000000.0;
Trace.Indent();
msg = "time=" + diff.ToString() + " sec";
Trace.WriteLine(msg);
Trace.Unindent();
Trace.Unindent();
diff = (end_t - start_draw_t)/10000000.0;
msg = "total time = " + diff.ToString() + " sec";
Trace.WriteLine(msg);
DrawLabels(labels_list,curr_lay,graphics,trans);
}
}
private bool DrawLabels(SortedList labels_list,CanvasLayer lay,Graphics graphics,System.Drawing.Drawing2D.Matrix trans)
{
// draw labels:
long label_start_t = DateTime.Now.Ticks;
System.Drawing.Font drawFont = new System.Drawing.Font("Arial", 6);
System.Drawing.SolidBrush drawBrush = new System.Drawing.SolidBrush(lay.LabelColor);
System.Drawing.SolidBrush fillBrush = new System.Drawing.SolidBrush(lay.LabelColorBg);
System.Drawing.StringFormat drawFormat = new System.Drawing.StringFormat();
System.Drawing.Pen pen = new Pen(Color.Blue);
ArrayList displayed_labels = new ArrayList();
foreach(int shape_id in labels_list.Keys)
{
CanvasItem item = (CanvasItem)(m_canvas_objects[shape_id]);
string label = item.layer.ShapeGetLabel(shape_id);
PointF [] src_pnts1 = {new PointF(item.X(),item.Y())};
trans.TransformPoints(src_pnts1);
float px = src_pnts1[0].X;
float py = src_pnts1[0].Y;
float angle = 0;
RectangleF lrect = new RectangleF(0,0,drawFont.Size*label.Length,
drawFont.Height);
ArrayList al = (ArrayList)labels_list[shape_id];
// get angle of the text from consequtive points:
if(al.Count > 0)
{
ArrayList segment = (ArrayList)al[0];
if(segment.Count > 1)
{
PointF p0 = (PointF)segment[0];
PointF p1 = (PointF)segment[1];
double dx = (p0.X-p1.X);
double dy = (p0.Y-p1.Y);
double dist = dx*dx+dy*dy;
// find biggest segment:
for(int i=1; i<segment.Count; i++)
{
PointF p_0 = (PointF)segment[i-1];
PointF p_1 = (PointF)segment[i];
dx = (p_0.X-p_1.X);
dy = (p_0.Y-p_1.Y);
double curr_dist = dx*dx+dy*dy;
if(curr_dist > dist)
{
dist = curr_dist;
p0 = p_0;
p1 = p_1;
}
}
if(dist == 0)
continue;
PointF [] src_pnts2 = {p0,p1};
trans.TransformPoints(src_pnts2);
px = (src_pnts2[0].X+src_pnts2[1].X)/2;
py = (src_pnts2[0].Y+src_pnts2[1].Y)/2;
angle = (float)(Math.Atan((src_pnts2[0].Y-src_pnts2[1].Y)/(src_pnts2[0].X-src_pnts2[1].X))*180/3.14);
if(this.m_debug)
{
graphics.DrawLine(pen,new Point((int)src_pnts2[0].X,(int)src_pnts2[0].Y),
new Point((int)src_pnts2[1].X,(int)src_pnts2[1].Y));
}
}
}
RotatedRectangle drect = new RotatedRectangle(lrect,px,py,angle);
bool intersected = false;
foreach(RotatedRectangle dr in displayed_labels)
{
if(dr.IsIntersected(drect))
intersected = true;
}
if(intersected)
continue;
else
displayed_labels.Add(drect);
bool do_rotation = true;
float off_x=0;
float off_y=0;
if(Math.Abs(angle) < 5)
{
do_rotation = false;
off_x=px;
off_y=py;
}
if(do_rotation)
{
// translate graphics to the text start point:
graphics.TranslateTransform(px,py);
// calculate angle:
graphics.RotateTransform(angle);
}
if(lay.FillLabelBg)
{
graphics.FillRectangle(fillBrush,lrect.Left+off_x,
lrect.Top+off_y,lrect.Width,lrect.Height);
}
graphics.DrawString(label,
drawFont,drawBrush,
off_x,off_y,drawFormat);
if(this.m_debug)
graphics.DrawRectangle(pen,lrect.Left+off_x,
lrect.Top+off_y,lrect.Width,lrect.Height);
if(do_rotation)
{
// translate back
graphics.RotateTransform(-angle);
graphics.TranslateTransform(-px,-py);
}
}
long label_end_t = DateTime.Now.Ticks;
double label_diff = (label_end_t - label_start_t)/10000000.0;
Trace.Indent();
string label_msg = "label time["+displayed_labels.Count.ToString()+"]=" + label_diff.ToString() + " sec";
Trace.WriteLine(label_msg);
Trace.Unindent();
drawFont.Dispose();
drawBrush.Dispose();
drawFormat.Dispose();
pen.Dispose();
fillBrush.Dispose();
return true;
}
public void LoadImage(Image img)
{
if(picture != null)
picture.Dispose();
picture = img;
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(BoundingBox()));
}
public void LoadImage(System.IO.Stream stream)
{
if(picture != null)
picture.Dispose();
picture = System.Drawing.Image.FromStream(stream);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(BoundingBox()));
}
public void LoadImage(string fname)
{
picture = System.Drawing.Image.FromFile(fname);
if(ModelChangedEvent != null)
ModelChangedEvent(this,new ModelChangedEventArgs(BoundingBox()));
}
}
}