|
Îåêßíçóå áðü ôï ìÝëïò vtops. Τελευταία δημοσίευση από το μέλος KelMan στις 29-10-2011, 17:04. Υπάρχουν 4 απαντήσεις.
-
27-10-2011, 22:24
|
-
28-10-2011, 09:38
|
-
28-10-2011, 21:23
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Σίγουρα μπορεί να γίνει με διάφορες τεχνολογίες όπως GDI+ και DirectX (Managed DirectX για την περίπτωσή σου) αλλά θα σου πρότεινα να ανέβεις ένα σκαλί στο abstraction και να πας σε WPF ή σε Silverlight. Με αυτόν τον τρόπο θα μπορείς να δουλέψεις με τα objects κανονικά, με τα properties και τα events τους και τα όλα τους. Για παράδειγμα, θα μπορεί η "τελεία" να κάνει raise event ότι έγινε κλικ πάνω της και να μην ασχολείσαι συνεχώς με το σε τι συντεταγμένες έχει γίνει το κλικ και τι έχει από κάτω. Από εκεί και πέρα το να ζωγραφίσεις μια ευθεία από τις συντεταγμένες (Χ1,Υ1) στις (Χ2,Υ2), ας πούμε με WPF ή Silverlight, είναι γελοίο, είτε με XAML:
<Line
X1="10" Y1="10" X2="50" Y2="50" Stroke="Black" StrokeThickness="4" />
είτε με κώδικα
var myLine = new Line(); myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue; myLine.X1 = 1; myLine.X2 = 50; myLine.Y1 = 1; myLine.Y2 = 50; myLine.HorizontalAlignment = HorizontalAlignment.Left; myLine.VerticalAlignment = VerticalAlignment.Center; myLine.StrokeThickness = 2; ...
Vir prudens non contra ventum mingit
|
|
-
29-10-2011, 14:07
|
-
Libra Storm
-
-

-
Μέλος από τις 28-01-2011
-
Χαϊδάρι
-
Δημοσιεύσεις 142
-
-
|
Graph δηλαδή πέπει να φτιάξεις, με τελείες (Nodes) και γραμμές. Δεν γνωρίζω καμία κλάση γιά graphs αλλά όσον αφορά τον σχεδιασμό κύκλων και γραμμών το System.drawing namespace έχει ότι χρειάζεσαι και πολλά άλλα. Αυτό έιναι ένα Game στην ουσία με rules και levels. Δες αν θέλεις τον παρακάτω κώδικα που Δημιουργεί και μετακινεί nodes με δεξί click και τους ενώνει με αριστερό. Ο παρακάτω κώδικας δεν είναι optimized αλλά μπορείς να πάρεις μία ιδέα. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
| using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
using System.Linq ;
using System.Text ;
using System.Windows.Forms ;
using System.Drawing.Drawing2D ;
namespace Graph // To όλο "σκηνικό" λέγεται και Graph.
{
// Θα πρέπει να βάλεις τον κώδικα αυτόν σε custom control και όχι σε φόρμα κατ' ευθείαν.
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent() ;
// Double Buffering
// Flickering reduction. Τεχνική που βοηθάει να βγει το "τρεμούλιασμα"
this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true) ;
}
private List<Node> _Nodes = new List<Node>() ; // Οι Τελείες λέγονται και Nodes.
private Line _TempLine ; // Η προσωρινή γραμμή που εμφανίζεται όταν ξεκινάμε να ενώσουμε 2 τελείες ( Nodes ).
private Node _SelectedNode ; // Το node που επιλέξαμε ή μόλις δημιουργήσαμε ( με δεξί click ).
protected override void OnPaint(PaintEventArgs e)
{
Pen pen = new Pen(Color.DarkOrange, 3.0f) ;
if (_TempLine != null)
_TempLine.Draw(e.Graphics) ;
foreach (Node node in _Nodes)
{
foreach (Node NeighborNode in node.Neighbors)
{
e.Graphics.DrawLine(pen, node.Centre, NeighborNode.Centre) ;
// Optimize
// 1. Δεν πρέπει ένα neighbor Node να ξαναζωγραφίζει μία γραμμή που έχει ήδη ζωγραφιστεί.
// 2. Parallel ΟΛΑ.
NeighborNode.Draw(e.Graphics) ;
}
node.Draw(e.Graphics) ;
}
pen.Dispose() ;
base.OnPaint(e) ;
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
_SelectedNode = this.GetNode(e.X, e.Y) ;
if (_SelectedNode == null)
{
Node node = new Node() ;
node.Location = new Point(e.X - node.Radius, e.Y - node.Radius) ;
_Nodes.Add(node) ;
_SelectedNode = node ;
this.Invalidate(false) ;
}
}
else if (e.Button == MouseButtons.Left)
{
_SelectedNode = this.GetNode(e.X, e.Y) ;
if (_SelectedNode !=null )
{
Line line = new Line() ;
line.StartLocation = new Point(_SelectedNode.Location.X + _SelectedNode.Radius,
_SelectedNode.Location.Y + _SelectedNode.Radius) ;
_TempLine = line ;
}
}
base.OnMouseDown(e) ;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
_SelectedNode.Location = new Point(e.X - _SelectedNode.Radius, e.Y - _SelectedNode.Radius) ;
// (Invalidate) Ανανέωση των αλλαγών. Αν έχεις πολλά nodes πρέπει να κάνεις invalidate με region και όχι όλο το control,
// κάνωντας Draw μόνο τα γειτονικά nodes και !!! τις γραμμές που περνάνε από τον ίδιο χώρο !!!.
// Γιά αυτόν τον λόγο φλικάρει αρκετά χωρίς DBuffer.
this.Invalidate(false) ;
}
else if (e.Button == System.Windows.Forms.MouseButtons.Left && _TempLine != null)
{
_TempLine.EndLocation = new Point(e.X, e.Y) ;
this.Invalidate(false) ;
}
base.OnMouseMove(e) ;
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (_TempLine != null)
{
Node targetNode = this.GetNode(e.X, e.Y) ;
if (targetNode != null && targetNode != _SelectedNode && !_SelectedNode.Neighbors.Contains(targetNode))
{
_SelectedNode.Neighbors.Add(targetNode) ;
if (!targetNode.Neighbors.Contains(_SelectedNode))
targetNode.Neighbors.Add(_SelectedNode) ;
}
else
{
System.Media.SystemSounds.Hand.Play() ;
System.Diagnostics.Debug.WriteLine("Invalid Join.") ;
}
_TempLine = null ;
this.Invalidate(false) ;
}
}
base.OnMouseUp(e) ;
}
private Node GetNode(int x, int y)
{
foreach (Node node in _Nodes)
// Picking
// Πολύ χρήσιμο method (IsVisible). Τσεκάρει αν κάποιο σημείο είναι μέσα στο path.
// Στην δική μας περίπτωση ελέγχει αν το ποντίκι είναι πάνω από ένα node.
if (node.Path.IsVisible(x, y))
return node ;
return null ;
}
}
// ---------------------------------------------------------------------------------------
// Σε αυτό το Class πρέπει να κάνεις implement Τo IDisposable γιατί
// Το Path Field κάνει expose ένα Disposable class (το GraphicsPath).
internal class Node
{
internal List<Node> Neighbors = new List<Node>() ;
internal readonly GraphicsPath Path = new GraphicsPath() ;
internal Point Location ;
internal int Radius = 8 ;
internal Point Centre
{
get
{
return new Point(this.Location.X + Radius, this.Location.Y + Radius) ;
}
}
internal void Draw(Graphics graphics)
{
Pen pen = new Pen(Color.DarkOrange, 3.0f) ;
Path.Reset() ;
graphics.SmoothingMode = SmoothingMode.HighSpeed ;
Path.AddEllipse(Location.X, Location.Y, Radius * 2, Radius * 2) ;
graphics.FillPath(Brushes.White, Path) ;
// Antialiasing
// Η γνωστή τεχνική και από τα παιγνίδια.
// Χοντρικά Κάνει τις διαγώνιες γραμμές πιό ομαλές αλλά τρώει performance.
graphics.SmoothingMode = SmoothingMode.AntiAlias ;
graphics.DrawPath(pen, Path) ;
pen.Dispose() ;
}
}
internal class Line
{
internal Point StartLocation ;
internal Point EndLocation ;
internal void Draw(Graphics graphics)
{
Pen pen = new Pen(Color.OrangeRed, 3.0f) ;
graphics.SmoothingMode = SmoothingMode.AntiAlias ;
graphics.DrawLine(pen, StartLocation.X, StartLocation.Y, EndLocation.X, EndLocation.Y) ;
pen.Dispose() ;
}
}
} // καλή επιτυχία |
|
|
-
29-10-2011, 17:04
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Χωρίς να θέλω να μειώσω σε τίποτα την απάντηση του Libra Storm και θέλοντας απλά να υπογραμίσω αυτά που γλυτώνεις με το Silverlight και το WPF, ορίστε μια απλοϊκή υλοποίηση σε WPF:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Ellipse selectedDot;
private Random rnd = new Random(System.Environment.TickCount);
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
for (var i = 1; i <= 10; i++)
{
var dot = new Ellipse
{
Height = 10,
Width = 10,
StrokeThickness = 2,
Fill = Brushes.Yellow,
Stroke = Brushes.Black,
Tag = i
};
var randomPoint = new Point(rnd.Next(5, (int)MainGrid.ActualWidth - 5), rnd.Next(5, (int)MainGrid.ActualHeight - 5));
dot.SetValue(Canvas.LeftProperty, randomPoint.X);
dot.SetValue(Canvas.TopProperty, randomPoint.Y);
MainGrid.Children.Add(dot);
dot.MouseUp += dot_MouseUp;
}
}
private void dot_MouseUp(object sender, MouseButtonEventArgs e)
{
var dot = (Ellipse)sender;
if (selectedDot != null)
{
selectedDot.Fill = Brushes.Yellow;
}
dot.Fill = Brushes.Blue;
if (selectedDot != null && dot != null)
{
if (Math.Abs((int)selectedDot.Tag - (int)dot.Tag) == 1)
{
AddNewLine((double)selectedDot.GetValue(Canvas.LeftProperty),
(double)dot.GetValue(Canvas.LeftProperty),
(double)selectedDot.GetValue(Canvas.TopProperty),
(double)dot.GetValue(Canvas.TopProperty));
}
}
selectedDot = dot;
}
private void AddNewLine(double x1, double x2, double y1, double y2)
{
var myLine = new Line();
myLine.Stroke = Brushes.LightSteelBlue;
myLine.X1 = x1;
myLine.X2 = x2;
myLine.Y1 = y1;
myLine.Y2 = y2;
myLine.StrokeThickness = 2;
MainGrid.Children.Add(myLine);
}
}
} |
Έχουμε 10 Ellipse objects που τοποθετούνται τυχαία πάνω στη φόρμα σε ένα Canvas (θα χρειαστεί να προσθέσεις ένα button και να κάνεις wire up το click event για να εμφανιστούν τα dots) και ο χρήστης μπορεί να ενώσει μόνο αυτά που σχετίζονται (μέσω του Tag property). Το θέμα δεν είναι τόσο σε αυτό το κομμάτι της λογικής, όσο τα πλεονεκτήματα αυτής της λύσης:
- Δεν χρειάζεται να ασχοληθείς με το rendering (antializing, double buffers, smoothing modes, κλπ)
- Δεν χρειάζεται να ασχοληθείς με το dispose των γραφικών στοιχείων
- Δεν χρειάζεται να ζωγραφίζεις τα πάντα κάθε φορά, μόνο τη γραμμή που σου χρειάζεται
- Το κάθε Ellipse κάνει raise event στο mouse up και έχεις απευθείας τις συντεταγμένες που του αντιστοιχούν χωρίς να ψάχνεις τίποτα
Vir prudens non contra ventum mingit
|
|
|
|
|