 |
|
 |
It is very wonderful but how can I lock the aspect ratio of selected rectangle?
|
|
|
|
 |
|
 |
I'm sorry: somehow, I just now noticed the notification email for your question.
I'll have to think about this.
|
|
|
|
 |
|
 |
What you'd want to do is modify the component's code to draw the selected rectangle at the desired aspect ratio, rather than by merely the current mouse coordinates, as it does now.
The component draws (and un-draws and redraws) the rectangle based on the mouse current (x, y) coordinates [typically, using them as the lower-right corner] in relation to the mouse starting (x, y) coordinates [typically, using them as the upper-left corner].
What you'd want to do is modify the code to use the mouse current (x, y) coordinates as the basis for computing an appropriate point for the new corner. Thus, sometimes the new-corner point would be in the for of (mouse-x, computed-y) and other times in the form of (computed-x, mouse-y). Each time you draw the rectangle, you'd need to determine which is appropriate.
The user of your modified component would see this programatic activity as being that the current mouse position is rarely at the exact corner of the drawn rectangle, but rather that his movement of the mouse is changing either the height or the width of the rectangle and that the other dimension changes automatically.
I haven't looked at my code for this component in quite a while, but I'm pretty sure it's written in such a way that you could incorporate your desired changes into the code in a fairly modular manner.
|
|
|
|
 |
|
 |
I have some free time, and since this is an interesting question you've asked, I've been thinking about it and playing with the idea in code.
Keep in mind that I wrote this project using the .NET 1.1, so the code doesn't current use generics, etc; therefore it contains essentially duplicate code for the left and right mouse buttons. For the test code I'm going to list, I'll assume the left mouse button. As a note, in my explanation I enclose variable names in square brackets in an attempt to differentiate them from regular text.
The changes you're asking about would have to be made in the class/method [TdhMarchingAnts_NativeWindow._parentControl_MouseMove()]
Consider this block of code in the method, as it currently stands:
#region if (leftMouseTracking)
if (leftMouseTracking) { if (!TdhMarchingAnts.IsReallyEmptyPoint(leftPathPoint2)) {
DrawReversibleRectangle(leftPathPoint1, leftPathPoint2); }
leftPathPoint2 = currentPoint; DrawReversibleRectangle(leftPathPoint1, leftPathPoint2); }
#endregion
What we want to do is replace the following line of code with new logic to set [leftPathPoint2] to a computed value, the computations being based on the given aspect ratio and the mouse movement.
leftPathPoint2 = currentPoint;
Or, rather, if no aspect ratio is given, then execute that line as it is, but if an aspect ratio is given, then execute the new logic.
Also, to do this correctly, the test-code I'm going to post is too simplistic. For instance, in the method, the block of code to ensure that [currentPoint] is within [this._parentControl.ClientRectangle] was executed when the [currentPoint] values were set. It seems to me that this logic would also need to be executed against the computed point's values.
|
|
|
|
 |
|
 |
Following is changed logic for the section of code I pointed out previously. It's still a bit rough -- for instance, the computed rectangle based on the given aspect ratio may "overshoot" the client area of the control when it is drawn.
As you can see, I didn't replace (or technically modify) the line of code I'd drawn your attention to; rather, I added now logic prior to it.
The variable [leftAspectRatio] (and [rightAspectRatio] for the corresponding code for the right mouse button) is a new global variable to control drawing the rectangle with a given aspect ratio. Obviously, there would need to be canges to the public interface to get a value into this field. When the value of [leftAspectRatio] < 1.0, the rectangle will have a portait orientation. When the value of [leftAspectRatio] > 1.0, the rectangle will have a landscape orientation.
#region if (leftMouseTracking)
if (leftMouseTracking) { if (!TdhMarchingAnts.IsReallyEmptyPoint(leftPathPoint2)) {
DrawReversibleRectangle(leftPathPoint1, leftPathPoint2); }
if (leftAspectRatio != 0f)
{
#region How far (and which directions) has the mouse moved from the initial point?
int xMove = currentPoint.X - leftPathPoint1.X;
int xMoveAbs = System.Math.Abs(xMove);
int xMoveSign = 1;
if (xMove < 0)
{
xMoveSign = -1;
}
int yMove = currentPoint.Y - leftPathPoint1.Y;
int yMoveAbs = System.Math.Abs(yMove);
int yMoveSign = 1;
if (yMove < 0)
{
yMoveSign = -1;
}
System.Drawing.Point hitPoint = new System.Drawing.Point(currentPoint.X, currentPoint.Y);
#endregion
if (yMoveAbs >= xMoveAbs) {
currentPoint.X = leftPathPoint1.X + xMoveSign * (int)(yMoveAbs * leftAspectRatio);
if (System.Math.Abs(currentPoint.X - leftPathPoint1.X) < xMoveAbs)
{
currentPoint.X = hitPoint.X;
currentPoint.Y = leftPathPoint1.Y
+ yMoveSign * (int)(System.Math.Abs(hitPoint.X - leftPathPoint1.X) / leftAspectRatio);
}
}
else
{
currentPoint.Y = leftPathPoint1.Y + yMoveSign * (int)(xMoveAbs / leftAspectRatio);
if (System.Math.Abs(currentPoint.Y - leftPathPoint1.Y) < yMoveAbs)
{
currentPoint.Y = hitPoint.Y;
currentPoint.X = leftPathPoint1.X
+ xMoveSign * (int)(System.Math.Abs(currentPoint.Y - leftPathPoint1.Y) * leftAspectRatio);
}
}
}
leftPathPoint2 = currentPoint; DrawReversibleRectangle(leftPathPoint1, leftPathPoint2); }
#endregion
modified on Saturday, October 24, 2009 10:08 AM
|
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Well written and well-coded.
Jon
Smith & Wesson: The original point and click interface
|
|
|
|
 |
|
 |
Hmm, what is your recommendation for porting this over to ASP.NET so that it could be used in a web application?
Thanks for your input
|
|
|
|
 |
|
 |
Unfortunately, I can give you no useful input on that question. I hope you're able to pull it off (and if you do, be sure to submit an article).
|
|
|
|
 |
|
 |
Hi,
I have the weirdest request: is there a chance you could PM me your real name?
It's all I need. See, I would like to use your TDHMarchingAnts (which works absolutely fantastic!) in one of the code modules of my Master's thesis.
As you may know, in academia proper attribution is everything to avoid plagiarism - and I want to give honour and credit where honour and credit is due.
Meaning I need to say "TDHMarchingAnts in the public domain found on codeproject.com, original author ".
Thanks!
Mike
|
|
|
|
 |
|
 |
No need for a PM ... for one thing, since I am "no one," my name is no great secret ... for a second thing, even it it were a great secret, some months ago when I momentarily cared about trying to discuss Deep Things with the folk who frequent the SoapBox, someone there decided to make the effort to find out who I am and post the result.
I'm both mystified and gratified that someone would find my effort significant enough to use as part of his Master's thesis.
My name is "Troy Hailey."
|
|
|
|
 |
|
 |
Thanks for that!
No big mystery: Instead of re-inventing the wheel, I wanted to use a nice marching ants class for my project. If I didn't go and use a lot of "pre-assembled" modules, I could never focus on the actual research part of my thesis.
That's what code reuse is all about, after all!
Mike
|
|
|
|
 |
|
 |
Quite so; why re-invent the wheel? Though, "enhancing" a wheel one finds or borrows can be gratifying or useful.
Given your needs, I quite understand that you likely will not be trying to think of any ways to enhance this particular wheel. And then, considering that the class is written for a specific and limited task, it may be that the only logical enhancements to it are to make it more effient or to correct logic errors that I haven't yet discovered.
But, should it turn out that as you use the code in your thesis' project you think of a way it could be made better -- or if you discover that due to my status as a self-taught beginner (that is, programming with C# and using OOL concepts, not as a programmer) I've made some sort of programming error -- please let me know.
What's the point or purpose of the program you're writing?
|
|
|
|
 |
|
 |
It is an image-analytical project designed to allow for image-based searching of wines (!) within a catalog/collection.
While technology won't allow us (yet) to transport and store smells and tastes in a program, I am trying to determine whether it is possible to have a program "look" at a glass of wine (just as a wine drinker, like myself, would do) and compare it to others that are stored in image form.
Your class is being used to mark an "area of interest" in an image of wine in a glass, which is then processed with a variety of image-analytical processes. These results are then stored and used for comparison.
No, I didn't get a huge government grant for this.
Mike
|
|
|
|
 |
|
|
 |
|
 |
I love your attitude!
But seriously, you're welcome.
|
|
|
|
 |
|
 |
Hi,
First I'd like to thank you for putting up such an useful and nice looking control.
It's being quite helpful on some work I'm doing right now.
Anyway, I've encountered a little problem with it.
Probably I'm just doing something wrong, I'm quite new to c# and there are lots of things I still don't know how to properly do.
Anyway, I'm having some problems when closing my program.
I get an InvalidOperationException about different processes in _CommonDispose() function, exactly here:
if (!gblRunModeIs_DesignMode)
{
this._parentControl.Cursor = cursorOriginal;
}
Maybe it's a common error, because VS directs me here:
http://msdn2.microsoft.com/en-us/library/ms171728.aspx
I've read the page, but certainly I don't understand most of what it says
Here's how to reproduce the error under VS2005:
-Design a new form2 and attach it the marching ants
-From the main form, create a new instance of form2
-The error only shows up when the form2 is hidden, so either don't show it or hide it.
-Close the main application
-Voila
So,
Can anyone reproduce the error, or it's just me?
Is this a bug, or am I doing something wrong?
Commenting the line avoids the error, but I'm quite positive that's not an acceptable way
Thanks for everything,
Albert
|
|
|
|
 |
|
 |
I'm sorry you're getting an exception. I wasn't able to duplicate it -- but I also don't have VS2005 (perhaps the error you're getting is a combination of what my code is attempting to do in a VS2005 environment).
Try replacing this line:
this._parentControl.Cursor = cursorOriginal;
With the following:
if (this._parentControl.Visible)
{
this._parentControl.Cursor = cursorOriginal;
}
And please let me know if this resolves your problem, so that I can incorporate this code into the control.
-- modified at 16:47 Thursday 11th January, 2007
"Maybe it's a common error, because VS directs me here:
http://msdn2.microsoft.com/en-us/library/ms171728.aspx
I've read the page, but certainly I don't understand most of what it says "
That page is called "How to: Make Thread-Safe Calls to Windows Forms Controls" Now, the odd thing about this is that this particular control doesn't use (multi-)threading.
But, I don't doubt that "InvalidOperationException" is quite common; I expect it's a grab-bag exception. So, perhaps that page doesn't really have much to do with the exact error you're getting.
The page says, "If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state." Even though the marching-ants control doesn't use threading, it does sound as though the circumstance you're experiencing is due to something about your (hidden) form having been forced into "an inconsistent state"
|
|
|
|
 |
|
 |
Hi,
Many thanks for your prompt answer.
That code did solve the problem, but it kind of feels like cheating
The exact error I'm getting is "Control PicBox accessed from a thread other than the thread it was created on.", which is the error discussed in the link I posted before. Like you say, the form is probably carried to another thread when hidden.
In the same web there's this note:
"You can disable this exception by setting the value of the CheckForIllegalCrossThreadCalls property to false. This causes your control to run the same way as it would run under Visual Studio 2003."
Effectively, adding the line
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
also solves the problem and would explain why you're not getting the error, but I'd rather not add code like that unless I'm absolutely sure of what it involves, and in this case I'm clearly not.
So, unless someone wants to give a deep explanation on what is going on, I think I'll stick with your solution.
Again, thanks for your help.
Albert
|
|
|
|
 |
|
 |
"That code did solve the problem, but it kind of feels like cheating. "
No, it's not cheating, it's error-avoidance
Thanks for letting me know; I'll incorporate that check into my code.
BTW, I've been coding in C# for about 3 years and I still feel like a novice.
|
|
|
|
 |
|
 |
Well, after some more in-depth testing, it's still giving me the error.
It's only more difficult to predict, that's all
Seems that the thread restrictions have increased from VS2003 to VS2005.
Control.CheckForIllegalCrossThreadCalls = false; "solves" the problem, but:
-It's not available under 2003
-Doesn't actually solve the problem, just hides the exception. That can't be good...
Please take a look here when you find the time:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=630558&SiteID=1
Doing this kind of solves the problem:
if (!gblRunModeIs_DesignMode)
{
if (!this._parentControl.InvokeRequired)
this._parentControl.Cursor = cursorOriginal;
}
That is, if there's no need to invoke it, do the job.
Actually that's just part of it. The equivalent of "if (visible)" but working. If the invoke is required, you should (obviously) invoke it.
Lots of questions, like where to put the delegate, or even why are not other parts of the code failing under these restrictions, I totally ignore the answers for.
Anyway, hope the input helps and you can get the component to be "VS2005 compliant".
Oh, by the way, I've also had two or three OutOFMemory exceptions. I didn't mark the place, but I'll let you know if it happens again.
Regards,
Albert
|
|
|
|
 |
|
 |
"Well, after some more in-depth testing, it's still giving me the error.
It's only more difficult to predict, that's all"
Uggh! Don't you just hate that sort of error?
"Seems that the thread restrictions have increased from VS2003 to VS2005."
But, I'm not doing any threading in this code. Perhaps the RunTime is doing some behind-the-scenes threading. Or, perhaps the eror message you're getting is unrelated to the actual error-condition.
Also, keep in mind, the particular instruction generating the error for you is not the only place where the class 'TdhMarchingAnts_NativeWindow' sets [this._parentControl.Cursor]; or, for that matter, sets other properties of [this._parentControl].
For instance, the '_InitializeComponents()' method (which is executed by the class constructor, adds EventHandlers to the parent control). And, more explicitly related to the instruction giving you the problem, when the class is active on the control it's attached to, it sets the control's cursor at both the MouseDown and MouseUp events.
I may be wrong (it should go without saying), but I really do think you're being lead down a false trail.
"Control.CheckForIllegalCrossThreadCalls = false; "solves" the problem, but:
-It's not available under 2003
-Doesn't actually solve the problem, just hides the exception. That can't be good..."
Certainly; hiding a problem isn't equivalent to solving it.
"Lots of questions, like where to put the delegate, or even why are not other parts of the code failing under these restrictions, I totally ignore the answers for."
Precisely, "why are not other parts of the code failing under these restrictions." There is something more/else going on here.
"Oh, by the way, I've also had two or three OutOFMemory exceptions. I didn't mark the place, but I'll let you know if it happens again."
Thanks.
======================
Looking again at the code in question, I see that I forgot to ensure that [this._parentControl] is not a null reference, as, for instance, I do a few steps further at the point of removing the EventHandlers my code had added in the '_InitializeComponents()' method. Could the issue be related to this in some way?
I'm wondering whether the potential over-kill I am doing with trying to clean-up the resources I allocate may not be causing the problem. As I mentioned, I still consider myself a novice; and one the the things I know I don't have a firm grasp of is Finalization/Garbage-Collection. (Just over the weekend, I was reading about this again, and I still can't synthesize the various things I've read into a coherent whole).
Anyway, the '_CommonDispose()' method, in which you're getting the error, is executed from both 'Dispose()' and '~TdhMarchingAnts_NativeWindow()' (The rationale for having this code in the '_CommonDispose()' method was to avoid the potential introduction of coding errors or discrepancies in having to maintain two methods doing the same set of tasks).
Might it be that what is happening is that in the particular way you are using this class, when the 'Dispose()' method is executed, [this._parentControl] may or may not already have been GarbageCollected, and thus the instruction [this._parentControl.Cursor = cursorOriginal;] will sporadically and unpredictbly fail? This might also explain why the error reported to you has no rational (so far as I can see) relationship to the possibilities (that is, the error reported to you has to do with threading and my class doesn't use threading).
Realizing that I'd forgotten to ensure that [this._parentControl] is not a null reference, I've modified my code thusly; though, if what I'm thinking is correct, the [&& (this._parentControl.Visible)] check is pointless:
if (!gblRunModeIs_DesignMode
&& (this._parentControl != null)
&& (this._parentControl.Visible)
)
{
this._parentControl.Cursor = cursorOriginal;
}
Could you try this (and try it without the [&& (this._parentControl.Visible)] check) and let me know what you think?
-- modified at 12:48 Tuesday 16th January, 2007
|
|
|
|
 |
|
 |
Hey there seems to be an issue with this. If I click the "test as form component button" and then create a selection rectangle then draw a selection rectangle in the "test using the tdhMarchingAnts Component" form, the uncheck the second checkbox (middle one). I get a JIT exception and it all crashes. This also happens if I uncheck the top checkbox, the re-check it.
Any ideas ???
Try it youself and see if you can repeat it.
sacha barber
|
|
|
|
 |
|
 |
Right you are.
I've duplicated the error-condition (that was easy enough). I haven't yet looked at the code to correct the error, but I highly suspect that it's going to be a timing issue. Or rather, that I'm disposing of some graphics object out-of-sequence.
Thanks for noticing this and making me aware of it. I should have the corrected version uploaded possibly today (01/08) and certainly by tomorrow (01/09).
|
|
|
|
 |
|