I suggest having a way to rotate an image as you draw it to another window. With this you could have (for example) a "spinner" image that you could animate by rotating it a number of degrees/radians.
Miniwindow image rotation
Posted by Twisol on Wed 25 Aug 2010 10:37 PM — 31 posts, 111,264 views.
I suggest you use sprites instead.
When the animation is the same image, just rotated, using sprites seems like overkill. =/
I'm thinking about more than just file images, though. You can use WindowImageFromWindow() to get the contents of a miniwindow as an image.
Rotation can be useful in other ways, too. If you look at OpenGL, there's a glRotatef() function that affects how later operations are drawn, effecting a rotational effect (EDIT: brought to you by the department of redundancy department). If there was a way to rotate and draw images, you could take a similar approach, drawing something on a separate, invisible canvas and applying it to the main window.
I know of no way to draw rotated rectangles, either. You could argue that a diamond can be drawn with lines, but then what do you say about using WindowCircleOp to draw a filled (textured) rectangle?
If you can rotate an image, you can rotate anything, because windows can be converted to images. Rotation is genuinely useful, and it makes many tasks easier. My only concern is how hard it might be to integrate into the current API... but lets deal with that only if we decide this is worth having.
I'm thinking about more than just file images, though. You can use WindowImageFromWindow() to get the contents of a miniwindow as an image.
Rotation can be useful in other ways, too. If you look at OpenGL, there's a glRotatef() function that affects how later operations are drawn, effecting a rotational effect (EDIT: brought to you by the department of redundancy department). If there was a way to rotate and draw images, you could take a similar approach, drawing something on a separate, invisible canvas and applying it to the main window.
I know of no way to draw rotated rectangles, either. You could argue that a diamond can be drawn with lines, but then what do you say about using WindowCircleOp to draw a filled (textured) rectangle?
If you can rotate an image, you can rotate anything, because windows can be converted to images. Rotation is genuinely useful, and it makes many tasks easier. My only concern is how hard it might be to integrate into the current API... but lets deal with that only if we decide this is worth having.
I had one idea for adding this to the API. (Yes, I should practice what I preach, shush.) An image can have a rotational variable added to it, and a new function WindowImageRotate() would affect it. It would start at 0 (no rotation). When the image is later drawn with WindowImageDraw(Alpha), they would take this value into account, rotating the image before it's drawn.
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
Example:
No other function API is affected, so it's totally backwards-compatible. If you don't use WindowImageRotate, it's just not rotated.
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
Example:
WindowImageRotate("window", "image", 45) -- degrees, in this example
WindowDrawImage("window", "image", 0, 0, 0, 0, 1, 0, 0, 0, 0)No other function API is affected, so it's totally backwards-compatible. If you don't use WindowImageRotate, it's just not rotated.
If something is already feasible, what is wrong with the current feasibility?
David Haley said:
If something is already feasible, what is wrong with the current feasibility?
If something is already feasible, what is wrong with the current feasibility?
Sorry David, I don't understand what you mean. Rotating images is effectively impossible right now. I thought I explained why it would be a very good thing to have in my second post, too. :S Can you elaborate?
Twisol said:
I suggest having a way to rotate an image as you draw it to another window. With this you could have (for example) a "spinner" image that you could animate by rotating it a number of degrees/radians.
I suggest having a way to rotate an image as you draw it to another window. With this you could have (for example) a "spinner" image that you could animate by rotating it a number of degrees/radians.
I was about to reply "absolutely not, it would be too slow", when I discovered the SetWorldTransform API. This simplifies things considerably.
See:
http://msdn.microsoft.com/en-us/library/dd145104%28VS.85%29.aspx
I had thought it would be nice to have (say) rotated text, but thought the overhead would be prohibitive. However it seems not.
Proviso - this API only works on Windows 98 upwards, however I think most people have moved on from Windows 95 by now, eh?
Twisol said:
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
I can't say I agree with this bit.
For maximum generality I am experimenting with setting up a translation matrix (see the API documentation mentioned above). This lets you set up 6 numbers, M11, M12, M21, M22, Dx and Dy.
Effectively this lets you control the translation between x and x' as a multiplication of the old x/y position with a multiplier (applied independently to x and y) followed by a displacement.
In a nutshell, this lets you scale, rotate, shear or reflect depending on the six values you supply.
http://msdn.microsoft.com/en-us/library/dd145228%28v=VS.85%29.aspx
So this gives you so much more than mere rotation, you could scale as well (or reflect).
I think for simplicity we will assume that the source image is to be completely applied to the destination (in other words, excluding sub-images), and at a given point.
You are correct that with virtually any transformation (that is, excluding mere reflection) some clipping or resizing will be required. However that is really just a case of having the room available to start with.
So, for example, if I had a long, thin image with the word "inventory" in it, and wanted to rotate it 90 degrees then the destination would need to be as deep as the source is wide, for it to fit.
So, what, we get WindowImageTransform?
...
You rule.
...
You rule.
Nick Gammon said:
I can't say I agree with this bit.
For maximum generality I am experimenting with setting up a translation matrix (see the API documentation mentioned above). This lets you set up 6 numbers, M11, M12, M21, M22, Dx and Dy.
Effectively this lets you control the translation between x and x' as a multiplication of the old x/y position with a multiplier (applied independently to x and y) followed by a displacement.
In a nutshell, this lets you scale, rotate, shear or reflect depending on the six values you supply.
http://msdn.microsoft.com/en-us/library/dd145228%28v=VS.85%29.aspx
So this gives you so much more than mere rotation, you could scale as well (or reflect).
I think for simplicity we will assume that the source image is to be completely applied to the destination (in other words, excluding sub-images), and at a given point.
You are correct that with virtually any transformation (that is, excluding mere reflection) some clipping or resizing will be required. However that is really just a case of having the room available to start with.
So, for example, if I had a long, thin image with the word "inventory" in it, and wanted to rotate it 90 degrees then the destination would need to be as deep as the source is wide, for it to fit.
Twisol said:
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
The image isn't -actually- rotated when WindowImageRotate() is used, because due to the rectangular nature of images, that would either clip the image's corners or require a resizing of the canvas. It's simpler just to store a rotational value, and use that when the image is drawn.
I can't say I agree with this bit.
For maximum generality I am experimenting with setting up a translation matrix (see the API documentation mentioned above). This lets you set up 6 numbers, M11, M12, M21, M22, Dx and Dy.
Effectively this lets you control the translation between x and x' as a multiplication of the old x/y position with a multiplier (applied independently to x and y) followed by a displacement.
In a nutshell, this lets you scale, rotate, shear or reflect depending on the six values you supply.
http://msdn.microsoft.com/en-us/library/dd145228%28v=VS.85%29.aspx
So this gives you so much more than mere rotation, you could scale as well (or reflect).
I think for simplicity we will assume that the source image is to be completely applied to the destination (in other words, excluding sub-images), and at a given point.
You are correct that with virtually any transformation (that is, excluding mere reflection) some clipping or resizing will be required. However that is really just a case of having the room available to start with.
So, for example, if I had a long, thin image with the word "inventory" in it, and wanted to rotate it 90 degrees then the destination would need to be as deep as the source is wide, for it to fit.
([EDIT]: I think I might've misunderstood you here; if I'm way off here, can you set me straight?)
Well, I realize that nothing prevents it from working if you apply the transformation immediately. My thought was that when you apply a transformation, you would only store the transformation matrix with the image; when you draw the image, -then- the matrix would be applied. Basically, the image clipping is based on the size of the target canvas rather than that of the source canvas.
If you have to explicitly size a window so you're sure you can rotate it to any angle you want, I'd think it would be a bit of a hassle. :S And the only difference is when you apply the transformation.
I am making a separate function WindowTranslateImage that lets you copy/transform from an image into the miniwindow via a specified set of 6 transformation parameters (two multiplications and one addition per x and y).
This avoids the complexity of also trying to select subimages, or have scaling applied via the normal WindowDrawImage.
You don't have to explicitly size anything, however if the destination is not the right size, the image won't fit (same as at present, really).
For example, if you make Dx and Dy large (eg. 1000) so that the image pixels are moved off the right of the bitmap, well you won't see it. So don't do that.
I don't think there is a simple generic solution that guarantees you will see the resulting image. A lot depends on the scaling, the rotation and the displacement.
Effectively the Dx and Dy parameters will control what you are rotating around (assuming you are rotating at all). With some values it will rotate around a corner, with others, around the center, with others around some other point.
This avoids the complexity of also trying to select subimages, or have scaling applied via the normal WindowDrawImage.
You don't have to explicitly size anything, however if the destination is not the right size, the image won't fit (same as at present, really).
For example, if you make Dx and Dy large (eg. 1000) so that the image pixels are moved off the right of the bitmap, well you won't see it. So don't do that.
I don't think there is a simple generic solution that guarantees you will see the resulting image. A lot depends on the scaling, the rotation and the displacement.
Effectively the Dx and Dy parameters will control what you are rotating around (assuming you are rotating at all). With some values it will rotate around a corner, with others, around the center, with others around some other point.
Twisol said:
Well, I realize that nothing prevents it from working if you apply the transformation immediately. My thought was that when you apply a transformation, you would only store the transformation matrix with the image; when you draw the image, -then- the matrix would be applied. Basically, the image clipping is based on the size of the target canvas rather than that of the source canvas.
Well, I realize that nothing prevents it from working if you apply the transformation immediately. My thought was that when you apply a transformation, you would only store the transformation matrix with the image; when you draw the image, -then- the matrix would be applied. Basically, the image clipping is based on the size of the target canvas rather than that of the source canvas.
Imagine you have cut out a small picture and have a large piece of paper on your desk. You can rotate the picture, and then throw it down somewhere onto the paper. The first four parameters control the rotation and the last two where it ends up, translated in the x and y direction.
If it happens to land on the paper on your desk, so it is completely on top of it, well and good. If it is off to one side it might be clipped.
Okay, I see. This new function actually transforms and draws at the same time? That's all that had confused me, I get it now.
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page). So, naturally, some content would not fall on the page. And no desk was involved. (I think that would be an extremely bad idea.)
The reason I wanted to separate the transformation from the drawing is because we already have, uh... three(?) other image-drawing functions: WindowDrawImage, WindowDrawImageAlpha, and WindowImageOp. Setting the transform separately means these three functions can still be used with transformations.
I think the term is "orthogonality".
Nick Gammon said:
If it happens to land on the paper on your desk, so it is completely on top of it, well and good. If it is off to one side it might be clipped.
If it happens to land on the paper on your desk, so it is completely on top of it, well and good. If it is off to one side it might be clipped.
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page). So, naturally, some content would not fall on the page. And no desk was involved. (I think that would be an extremely bad idea.)
The reason I wanted to separate the transformation from the drawing is because we already have, uh... three(?) other image-drawing functions: WindowDrawImage, WindowDrawImageAlpha, and WindowImageOp. Setting the transform separately means these three functions can still be used with transformations.
I think the term is "orthogonality".
Twisol said:
I suggest having a way to rotate an image as you draw it to another window.
I suggest having a way to rotate an image as you draw it to another window.
That's what I did. You rotate it as you copy it to the window.
Quote:
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page).
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page).
Well that's a bit fiddlier. If you mean transforming bits of the page then what gets left behind under the rotated bit?
And I'm not too keen on having some sort of mode that gets stored with an image (like a rotation) because then you will never really be sure if someone earlier on set up a rotation on it. Having to explicitly transform/rotate as a single operation makes it clear that you are rotating right now.
If you look at the source, right now images are just bitmaps associated with an image name. There is no meta-data stored with them.
I'm not trying to replace Photoshop here (well, not yet).
With this new function you could rotate maps (eg. so that "up" is the way you are facing), or rotate text so labels could run sideways (assuming you pre-draw the text into a temporary window).
Reflection and shearing could be used to make nice shadows, but again, let's not get too far ahead of ourselves. This isn't supposed to be a 3D client.
Having said that though, with the transformation matrix you could somewhat more easily bang images onto a window with some sort of perspective (or I think you could, this is starting to stretch my knowledge of 3D maths, and matrices).
If you want to give your brain a work-out, read this about matrices:
http://en.wikipedia.org/wiki/Matrix_(mathematics)
A little way down (Linear transformations) it mentions how matrices can be used to shear, rotate, squeeze, scale and flip.
http://en.wikipedia.org/wiki/Matrix_(mathematics)
A little way down (Linear transformations) it mentions how matrices can be used to shear, rotate, squeeze, scale and flip.
Nick Gammon said:
And I'm not too keen on having some sort of mode that gets stored with an image (like a rotation) because then you will never really be sure if someone earlier on set up a rotation on it. Having to explicitly transform/rotate as a single operation makes it clear that you are rotating right now.
Quote:
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page).
To put it in perspective, I thought you were talking about transforming the content of the page (without moving the page).
And I'm not too keen on having some sort of mode that gets stored with an image (like a rotation) because then you will never really be sure if someone earlier on set up a rotation on it. Having to explicitly transform/rotate as a single operation makes it clear that you are rotating right now.
Actually, I think that's a benefit. You can compose multiple transformations together by multiplying the matrices, so theoretically you could apply a scaling operation in one part of the code, then a rotational operation elsewhere. Assuming WindowImageTransform would multiply rather than replace, that is.
OpenGL - at least, its immediate mode - uses this a lot. It's really nice.
Nick Gammon said:
If you look at the source, right now images are just bitmaps associated with an image name. There is no meta-data stored with them.
If you look at the source, right now images are just bitmaps associated with an image name. There is no meta-data stored with them.
I'll admit I haven't looked at the source yet about this; mostly I wanted to see whether anything might come of the idea.
Nick Gammon said:
With this new function you could rotate maps (eg. so that "up" is the way you are facing), or rotate text so labels could run sideways (assuming you pre-draw the text into a temporary window).
Reflection and shearing could be used to make nice shadows, but again, let's not get too far ahead of ourselves. This isn't supposed to be a 3D client.
With this new function you could rotate maps (eg. so that "up" is the way you are facing), or rotate text so labels could run sideways (assuming you pre-draw the text into a temporary window).
Reflection and shearing could be used to make nice shadows, but again, let's not get too far ahead of ourselves. This isn't supposed to be a 3D client.
These are all acceptable 2D operations. Genuinely useful ones, too. ;)
Nick Gammon said:
Having said that though, with the transformation matrix you could somewhat more easily bang images onto a window with some sort of perspective (or I think you could, this is starting to stretch my knowledge of 3D maths, and matrices).
Having said that though, with the transformation matrix you could somewhat more easily bang images onto a window with some sort of perspective (or I think you could, this is starting to stretch my knowledge of 3D maths, and matrices).
I'm not sure what the third dimension you're referring to is. We don't have a depth axis in miniwindows, unless it's emulated a la your exits visualization demo.
My knowledge of the graphical application of matrices is that you have the four corners of a quadrilateral, and the cells of a matrix define translations of these points to new locations, thus stretching and/or squeezing the quadrilateral. I could be way off, but that's how I understand it.
At any rate, I still think orthogonality of an API is a really good thing to shoot for. If we attach a transformation matrix to each image - initialized to the identity matrix, of course - then it can be managed separately from the current, existing functions which I'm sure you're loath to break backwards-compatibility or, and hopefully just as loath to reimplement their functionality in a set of new functions.
Twisol: I was referring to the fact that you were given a solution but deemed it "overkill". I was asking what about it was unacceptable to you.
Regarding matrices, I agree that transformations should not be stored on the images themselves. If you look at OpenGL, for instance, that's not how they do it either. You set up transformations, you draw some stuff, you might add more transformations, draw more stuff, etc. But you don't attach a transformation to an image, and then draw the image without knowing about its transformation.
If you need images to store their own transformation information, such that when rendered they apply it before drawing, then reverse it for subsequent operations, then I would suggest making that a feature of your API, with a sprite object of some sort that stores its own transformation for when it is rendered.
What you said about composing transformations does not appear to be relevant to storing transformations on images -- in fact, as you said, OpenGL composes transformations and this is useful, but it doesn't do it by storing them on images!
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
I don't really understand the comments about not seeing the resulting image. If you transformed it such that it doesn't appear in the view anymore, don't you expect to not see it? Isn't that exactly the desired behavior?
Regarding matrices, I agree that transformations should not be stored on the images themselves. If you look at OpenGL, for instance, that's not how they do it either. You set up transformations, you draw some stuff, you might add more transformations, draw more stuff, etc. But you don't attach a transformation to an image, and then draw the image without knowing about its transformation.
If you need images to store their own transformation information, such that when rendered they apply it before drawing, then reverse it for subsequent operations, then I would suggest making that a feature of your API, with a sprite object of some sort that stores its own transformation for when it is rendered.
What you said about composing transformations does not appear to be relevant to storing transformations on images -- in fact, as you said, OpenGL composes transformations and this is useful, but it doesn't do it by storing them on images!
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
I don't really understand the comments about not seeing the resulting image. If you transformed it such that it doesn't appear in the view anymore, don't you expect to not see it? Isn't that exactly the desired behavior?
David Haley said:
Twisol: I was referring to the fact that you were given a solution but deemed it "overkill". I was asking what about it was unacceptable to you.
Twisol: I was referring to the fact that you were given a solution but deemed it "overkill". I was asking what about it was unacceptable to you.
Sprite maps work in some instances. Only effectively for static data. I don't know why I said "overkill", I think I was thinking of the effort involved to duplicate the image and rotate it, and the size of the image involved. If you have a very long base image, managing the sprite map would be verrry interesting.
David Haley said:
Regarding matrices, I agree that transformations should not be stored on the images themselves. If you look at OpenGL, for instance, that's not how they do it either. You set up transformations, you draw some stuff, you might add more transformations, draw more stuff, etc. But you don't attach a transformation to an image, and then draw the image without knowing about its transformation.
Regarding matrices, I agree that transformations should not be stored on the images themselves. If you look at OpenGL, for instance, that's not how they do it either. You set up transformations, you draw some stuff, you might add more transformations, draw more stuff, etc. But you don't attach a transformation to an image, and then draw the image without knowing about its transformation.
But you do apply a transformation separately from drawing. With OpenGL, it's stored as the transformation matrix of the current context. I -would- like to have a transformation matrix stored with the window itself, but I'm just a little worried about possible confusion. It feels like it could be too easy to think the window itself is stretched/squashed/rotated, rather than the content. ([EDIT] And also easy to think it affects stuff drawn already.)
Hmm... Yeah, storing the matrix with the window would be better.
David Haley said:
If you need images to store their own transformation information, such that when rendered they apply it before drawing, then reverse it for subsequent operations, then I would suggest making that a feature of your API, with a sprite object of some sort that stores its own transformation for when it is rendered.
If you need images to store their own transformation information, such that when rendered they apply it before drawing, then reverse it for subsequent operations, then I would suggest making that a feature of your API, with a sprite object of some sort that stores its own transformation for when it is rendered.
Just to be clear here, you're talking about a sprite map image, where multiple "frames" of a sprite are stored in the same file?
David Haley said:
What you said about composing transformations does not appear to be relevant to storing transformations on images -- in fact, as you said, OpenGL composes transformations and this is useful, but it doesn't do it by storing them on images!
What you said about composing transformations does not appear to be relevant to storing transformations on images -- in fact, as you said, OpenGL composes transformations and this is useful, but it doesn't do it by storing them on images!
I'm not sure why it's not relevant. The same thing happens; the difference is that a different matrix is applied to each image. Now though, I do agree it's better stored with the miniwindow. I realized there would be an annoying problem passing around Images with my API, because when it came time to draw it, I'd have to load the image into the target window and reconstruct the matrix somehow (probably through a hackish WindowImageInfo call). Applying transformations to the window's transformation matrix just works.
David Haley said:
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
I figure that each miniwindow would effectively be its own context. I'm not sure when it would be useful to have multiple contexts for the same miniwindow?
David Haley said:
I don't really understand the comments about not seeing the resulting image. If you transformed it such that it doesn't appear in the view anymore, don't you expect to not see it? Isn't that exactly the desired behavior?
I don't really understand the comments about not seeing the resulting image. If you transformed it such that it doesn't appear in the view anymore, don't you expect to not see it? Isn't that exactly the desired behavior?
I can't remember what I said that you're referring to, a quote would be appreciated. But I think(?) I was talking about how the transformation stored with an image isn't applied until the image is actually drawn. Otherwise, I don't know, because I agree with you.
Twisol said:
I figure that each miniwindow would effectively be its own context. I'm not sure when it would be useful to have multiple contexts for the same miniwindow?
David Haley said:
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
If anything, the conclusion of your argument is that there should be an independent transform function that you use to set up a drawing context, not that there should be transformations stored on images.
I figure that each miniwindow would effectively be its own context. I'm not sure when it would be useful to have multiple contexts for the same miniwindow?
Actually, now that I think about it, that would be really useful. One of the things I'm already doing in my API is this:
w = Window.new(50, 50)
c = Context.new(w)
c.Pen("blue", "solid", 1)
c.MoveTo(25, 25)
c.DrawPie(20, 25, -- xradius, yradius
90, 0) -- angle of slice, rotation of the whole pie
w.Show()I'm basically separating the window drawing/content from the window's presence, and supporting multiple contexts. And I realized, if each window had just the one transformation context, I'm not sure how I could do this cleanly.
Funny how hypocritical that quote was. I said I don't see where multiple contexts would be useful, yet here I'd already done it...
[EDIT] Hah, and I realized I could implement MoveTo() using affine transforms too. This would actually be really, really clean using separate contexts.
Quote:
And I realized, if each window had just the one transformation context, I'm not sure how I could do this cleanly.
And I realized, if each window had just the one transformation context, I'm not sure how I could do this cleanly.
It seems that your idea of doing something cleanly is that other people do it for you. :-)
It's easy to do this cleanly: just keep track of the transformations that have been applied, and do the math on your end to pop or push transformations.
David Haley said:
It seems that your idea of doing something cleanly is that other people do it for you. :-)
It's easy to do this cleanly: just keep track of the transformations that have been applied, and do the math on your end to pop or push transformations.
Quote:
And I realized, if each window had just the one transformation context, I'm not sure how I could do this cleanly.
And I realized, if each window had just the one transformation context, I'm not sure how I could do this cleanly.
It seems that your idea of doing something cleanly is that other people do it for you. :-)
It's easy to do this cleanly: just keep track of the transformations that have been applied, and do the math on your end to pop or push transformations.
Whatever form the new function(s) take, I can deal with it. But I'd rather get as many issues on the table and out of the way as possible before the API is finalized, thanks. This is a discussion, after all. I'm not making any demands.
Oh, and we're already getting way more than I expected originally. We've gone from simple rotations to full-on affine transformations. Obviously, solutions I considered for rotations may not be completely applicable to managing matrices. But that is the point of a discussion: to come up with solutions and work out the kinks.
Come up with solutions, yes, but to which problems?
Twisol said:
At any rate, I still think orthogonality of an API is a really good thing to shoot for.
At any rate, I still think orthogonality of an API is a really good thing to shoot for.
If we are going to shoot for it, we better know what it is...
http://en.wikipedia.org/wiki/Orthogonality
http://en.wiktionary.org/wiki/orthogonal
I think you are probably using the meaning:
4. (software engineering) Able to be treated separately.
The content of the message should be orthogonal to the means of its delivery.
And I presume in the current context you are meaning that it would be nice if you could rotate/scale/shear everything, not just an image, even though you initially requested "a way to rotate an image as you draw it to another window."
The trouble is, that although doing what you initially asked for was quite simple, due to the existence of a transformation routine in the GDI library, making it "just work" for all the miniwindow functions is somewhat harder. One of the main reasons is that a lot of it (like blending images) works directly on the underlying pixel data (the R, G, B codes as bytes), as for example here:
#define Blend_It(Op) \
do \
if (Opacity < 1.0) \
for (i = 0; i < count; i++) \
pB [i] = Blend_Opacity (pA [i], pB [i], Op, Opacity);\
else\
for (i = 0; i < count; i ++)\
pB [i] = Op (pA [i], pB [i]);\
while (false)
Now to make that apply the transformations of the original x and y to the new x and y, inside this loop which is applying a function to each byte, could not only be complex to code, but slow to run.
Now it would be "nice" if it was all totally consistent, but I think we are shooting at "hey it would be nice if everything was orthogonal" just a nice design objective, rather than with any concrete example in mind for its use.
With its current implementation (and I will be testing today if it works with at least the on/off transparency) then you can make most of the rest of it work just by making a temporary copy. For example, instead of blurring and rotating at the same time, you could rotate first, and then blur.
Twisol said:
You can compose multiple transformations together by multiplying the matrices, so theoretically you could apply a scaling operation in one part of the code, then a rotational operation elsewhere.
You can compose multiple transformations together by multiplying the matrices, so theoretically you could apply a scaling operation in one part of the code, then a rotational operation elsewhere.
Subject to testing, I think a single operation would achieve this much, because by doing something like reversing the sign (eg. -cosine rather than cosine) you could reflect and rotate at the same time.
After all, multiplying matrices is effectively accumulating various operations, and in OpenGL the final transformation matrix would be the product of individual transformations.
Nick Gammon said:
Now to make that apply the transformations of the original x and y to the new x and y, inside this loop which is applying a function to each byte, could not only be complex to code, but slow to run.
Now to make that apply the transformations of the original x and y to the new x and y, inside this loop which is applying a function to each byte, could not only be complex to code, but slow to run.
It would add an extra calculation per point in that case, yes... In the case of WindowBlendImage, what if you drew the image to a temporary bitmap first, with the target window's transformation matrix applied, and then use the newly transformed image to blend with the target window?
WindowFilter probably isn't a big deal: it isn't drawing objects per se, it's just manipulating already-drawn pixels.
Nick Gammon said:
Now it would be "nice" if it was all totally consistent, but I think we are shooting at "hey it would be nice if everything was orthogonal" just a nice design objective, rather than with any concrete example in mind for its use.
Now it would be "nice" if it was all totally consistent, but I think we are shooting at "hey it would be nice if everything was orthogonal" just a nice design objective, rather than with any concrete example in mind for its use.
If we're just talking about pushing features out the door - the "as long as it works" method - sure, go ahead. But orthogonality makes it both easier to use and easier to maintain.
I found an interesting page on the orthogonality of a page here: http://www.faqs.org/docs/artu/ch04s02.html - Here's a quote that caught my eye:
Quote:
[...] As they point out, orthogonality reduces test and development time, because it's easier to verify code that neither causes side effects nor depends on side effects from other code - there are fewer combinations to test. If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
The concept of refactoring, which first emerged as an explicit idea from the 'Extreme Programming' school, is closely related to orthogonality. To refactor code is to change its structure and organization without changing its observable behavior. [...]
[...] As they point out, orthogonality reduces test and development time, because it's easier to verify code that neither causes side effects nor depends on side effects from other code - there are fewer combinations to test. If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
The concept of refactoring, which first emerged as an explicit idea from the 'Extreme Programming' school, is closely related to orthogonality. To refactor code is to change its structure and organization without changing its observable behavior. [...]
Nick Gammon said:
With its current implementation (and I will be testing today if it works with at least the on/off transparency) then you can make most of the rest of it work just by making a temporary copy. For example, instead of blurring and rotating at the same time, you could rotate first, and then blur.
With its current implementation (and I will be testing today if it works with at least the on/off transparency) then you can make most of the rest of it work just by making a temporary copy. For example, instead of blurring and rotating at the same time, you could rotate first, and then blur.
By using intermediate renderings? When you can multiply matrices together and get a unified matrix that does both at once? That's one of the lovely things I saw that ModifyWorldTransform() does (as well as allow you to reset to the identity matrix).
Nick Gammon said:
Subject to testing, I think a single operation would achieve this much, because by doing something like reversing the sign (eg. -cosine rather than cosine) you could reflect and rotate at the same time.
After all, multiplying matrices is effectively accumulating various operations, and in OpenGL the final transformation matrix would be the product of individual transformations.
Twisol said:
You can compose multiple transformations together by multiplying the matrices, so theoretically you could apply a scaling operation in one part of the code, then a rotational operation elsewhere.
You can compose multiple transformations together by multiplying the matrices, so theoretically you could apply a scaling operation in one part of the code, then a rotational operation elsewhere.
Subject to testing, I think a single operation would achieve this much, because by doing something like reversing the sign (eg. -cosine rather than cosine) you could reflect and rotate at the same time.
After all, multiplying matrices is effectively accumulating various operations, and in OpenGL the final transformation matrix would be the product of individual transformations.
That's what I mean. One part of the code can multiply in one matrix, to effect a rotation, and another part can add some scaling, and then somewhere else it can be drawn.
Please see the forum thread: http://gammon.com.au/forum/?id=10527.
Twisol said:
In the case of WindowBlendImage, what if you drew the image to a temporary bitmap first, with the target window's transformation matrix applied, and then use the newly transformed image to blend with the target window?
In the case of WindowBlendImage, what if you drew the image to a temporary bitmap first, with the target window's transformation matrix applied, and then use the newly transformed image to blend with the target window?
Ah, no. You would blend with the wrong (untranslated) pixels.
Nick Gammon said:
(post=10527)
(post=10527)
It's anything but orthogonal. I grant that, yes, it's exactly what I originally asked for. But if you agree that something like the below would be better - and after reading what I mentioned about orthogonality - then I would be glad to implement it for you, subject to peer review of course.
My preferred API would be:
WindowAffine(window, mode, m11, m12, m21, m22, dx, dy)
WindowAffineLoadIdentity(window)where all parameters are self-explanatory, except 'mode' which is:
0 - right-multiply
1 - left-multiplyEDIT: And associated WindowInfo selectors.
Nick Gammon said:
Ah, no. You would blend with the wrong (untranslated) pixels.
Twisol said:
In the case of WindowBlendImage, what if you drew the image to a temporary bitmap first, with the target window's transformation matrix applied, and then use the newly transformed image to blend with the target window?
In the case of WindowBlendImage, what if you drew the image to a temporary bitmap first, with the target window's transformation matrix applied, and then use the newly transformed image to blend with the target window?
Ah, no. You would blend with the wrong (untranslated) pixels.
Huh. Could you please explain that for me? I thought that by translating the image to a scratch window, then blending with the target window, it would have worked.
The scratch window would be rotated. The target window (which you blend into) would not be rotated. Thus you are blending the wrong pixels.
Possibly you might rotate the target too, into another scratch window, rotate the source into a scratch window, blend the rotated versions, and then somehow copy the results back (but only the exact pixels that are affected) into the original target window.
Apart from wanting orthogonality, I can't see a huge use for this.
But the underlying code would not be orthogonal. To do what you suggest is simply moving the problem from where you see it, to inside MUSHclient. You still have the quite probably possibility that some combination of blending, rotation, translation etc. will fail in some unexpected way.
Possibly you might rotate the target too, into another scratch window, rotate the source into a scratch window, blend the rotated versions, and then somehow copy the results back (but only the exact pixels that are affected) into the original target window.
Apart from wanting orthogonality, I can't see a huge use for this.
Quote:
If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
But the underlying code would not be orthogonal. To do what you suggest is simply moving the problem from where you see it, to inside MUSHclient. You still have the quite probably possibility that some combination of blending, rotation, translation etc. will fail in some unexpected way.
Nick Gammon said:
The scratch window would be rotated. The target window (which you blend into) would not be rotated. Thus you are blending the wrong pixels.
Possibly you might rotate the target too, into another scratch window, rotate the source into a scratch window, blend the rotated versions, and then somehow copy the results back (but only the exact pixels that are affected) into the original target window.
The scratch window would be rotated. The target window (which you blend into) would not be rotated. Thus you are blending the wrong pixels.
Possibly you might rotate the target too, into another scratch window, rotate the source into a scratch window, blend the rotated versions, and then somehow copy the results back (but only the exact pixels that are affected) into the original target window.
But... huh, I don't see any reason why you'd want the target to be rotated. As far as I can see, you'd want the image, say, rotated, and then blended with whatever it matches up to now.
Nick Gammon said:
But the underlying code would not be orthogonal. To do what you suggest is simply moving the problem from where you see it, to inside MUSHclient. You still have the quite probably possibility that some combination of blending, rotation, translation etc. will fail in some unexpected way.
Quote:
If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
If it breaks, orthogonal code is more easily replaced without disturbance to the rest of the system. Finally, orthogonal code is easier to document and reuse.
But the underlying code would not be orthogonal. To do what you suggest is simply moving the problem from where you see it, to inside MUSHclient. You still have the quite probably possibility that some combination of blending, rotation, translation etc. will fail in some unexpected way.
Still, the problem is isolated to the drawing functions themselves, because we know the matrix functions work.
And yes, I'd rather the client handle it, rather than force the developer to use a limiting API.
Twisol said:
But... huh, I don't see any reason why you'd want the target to be rotated. As far as I can see, you'd want the image, say, rotated, and then blended with whatever it matches up to now.
But... huh, I don't see any reason why you'd want the target to be rotated. As far as I can see, you'd want the image, say, rotated, and then blended with whatever it matches up to now.
Oh yeah, good point. But still code like this becomes a lot more complex:
for (i = 0; i < count; i ++)
pB [i] = Op (pA [i], pB [i]);
In this case I have two pointers, both to the bitmaps which I think have been normalized to the same size. But now "i" will be jumping all over the place for the source at least, and in some cases at least may well be off-screen. So you then have to define what it means to take (say) the average of two pixels where one doesn't exist because it has been rotated off the bitmap.
Quote:
And yes, I'd rather the client handle it, rather than force the developer to use a limiting API.
And yes, I'd rather the client handle it, rather than force the developer to use a limiting API.
Show me a concrete example involving MUD games (eg, player inventory, battle gauges) where this is a "limiting API". Not just for you to write a generic graphical API. Like I said before, I'm not trying to rewrite Photoshop.
Nick Gammon said:
Show me a concrete example involving MUD games (eg, player inventory, battle gauges) where this is a "limiting API". Not just for you to write a generic graphical API. Like I said before, I'm not trying to rewrite Photoshop.
Quote:
And yes, I'd rather the client handle it, rather than force the developer to use a limiting API.
And yes, I'd rather the client handle it, rather than force the developer to use a limiting API.
Show me a concrete example involving MUD games (eg, player inventory, battle gauges) where this is a "limiting API". Not just for you to write a generic graphical API. Like I said before, I'm not trying to rewrite Photoshop.
Perhaps I should have said unwieldy, because that's what the whole of the miniwindow API is right now. Which is why I'm trying to wrap it in something easier to grok, something rather more orthogonal.
At this point, thanks to your advice, I think I can do most things using a rough combination of WindowTranslateImage and WindowBlendImageAlpha. The fact that I have to strain and twist the API to do this just bothers me. Maybe there's no other way because the client architecture is fifteen years of collected detritus and maintenence? It just rubs me the wrong way, when I feel it could be easier.
I'm going to tinker with implementing it myself, just for kicks. No ETA though.
Thanks for all the help, Nick.