-
-
Notifications
You must be signed in to change notification settings - Fork 303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow easier translation/rotation of groups of primitives #194
Comments
Thanks for opening this! Totally agreed there needs to be an easier solution for rotating multiple primitives at once! While the processing approach of using a global rotation is very easy to add in and is easier to use with small sketches, it can be tricky to keep track of as programs grow and it can get easy to unexpectedly break things by adding or removing rotations or refactoring. Maybe we can achieve something similar that feels as easy to use by adding in some explicit translation/rotation types (matrices under the hood) that can be chained together and used to position primitives as an alternative to the positioning and orientation methods. |
@mitchmindtree I haven't personally encountered the problem of losing track since most usage of that feature goes along the lines of:
Note that that code above doesn't need to start from an "identity matrix" state, it can be put in a function and used to draw sub-objects of objects for example. I think it would be useful to enforce/automate this pushing/popping using a constructor and destructor though. |
I agree that that global translates are easier with simple projects but they can get hard to track as the project grows. Especially with multiple threads. |
@freesig Have you considered the Canvas solution? What do you think would be the pros/cons of that? To elaborate, Canvas can mimic the API of Draw and also have some sort of So you would do something along the lines of let draw = ...;
let my_object = /*some way to get a canvas? maybe Canvas::new or Canvas::from_wh()*/;
my_object.primitive().position(...);
my_object.primitive().position(...);
my_object.primitive().position(...);
draw.canvas(my_object).position(...).rotate(...).scale(...); |
@SuperCuber I really like the idea behind this and will certainly keep it in mind when I get around to addressing this :) |
We've just been discussing this a little more on the slack. Here's a little rough snippet of what I'm imagining: let draw2 = draw.rotate(r).translate(xy).scale(s);
// You can now use all the same methods as draw but they will be translated/oriented/scaled via an inner transform matrix.
draw2.line().points(a, b);
draw2.rect();
// Then we can easily continue drawing with the original, untransformed draw.
draw.polygon().points(ps).color(red);
//etc Transformations could be nested by calling the transform methods on the already transformed draw instance. |
Possible use case to consider: having a function that draws an object. impl CustomObject {
fn draw(&self, transformed: /*the type*/) {
// All drawing is relative to (0,0)
transformed.primitive().xy(...);
// Can also draw sub-custom-objects
self.custom_sub_object.draw(transformed.translate(...));
}
} Another proposal that I haven't considered before (and might be already implemented in some form?) is to let structs implement a trait Drawable {
fn draw(&self, draw: Draw) {
// draw relative to (0,0)
}
} And then you can do something like let draw = ...;
let my_object = ...;
draw.drawable(my_object).xy(...); Maybe there is a way to even combine those two approaches... |
This overhauls the `Draw` API with some pretty major changes. The largest of which, is that group transforms, blend modes and scissors are now possible via the new `draw.transform(mat4)`, `draw.blend(blend_desc)` and `draw.scissor(rect)` methods. Each of these methods return a new `Draw` instance of the same type, but with the given transform/blend/scissor applied! See [this comment](nannou-org#194 (comment)) for a short demo of what this looks like. None of the examples have been updated to take advantage of this yet, though there are many nature of code and generative design examples that have been awaiting these changes. In order to enable these changes, the `Draw` backend has overgone a major overhaul. Rather than rendering primitives when they are dropped, we now instead create an inner list of `DrawCommand`s. These are high-level commands that store minimal data and when combined, completely describe the users drawing. The `draw::Renderer` now digests a list of `DrawCommand`s, rather than taking the mesh directly. This allows to provide the primitives access to more useful state/caches/buffers during their rendering process. This also allows the `draw::Renderer` to generate a much more advanced render pass by creating unique `RenderPipeline`s per blend mode, unique `BindGroup`s per unique texture view (to-be-implemented) and switching between scissors during the render pass. These changes have also allowed for providing access to a glyph cache to the `Text` primitive. This means that, when `draw.text(str)` is used, glyphs associated with characters of text will now be cached on the GPU for efficient re-use. This means much faster drawing of large amounts of text than was previously possible. While this commit includes many additions, it also includes the removal of a significant number of positioning, sizing and orientation methods. Specifically, all methods that used to describe some relative transformation (e.g. "down_from", "align_right", etc) have been removed in favour of the new approach of using transformations. While these old relative positioning methods were occasionally useful, the new group transformations are strictly more flexible and provide a more familiar API to users coming from processing, openframeworks, etc. Previously, the **Draw** API used to construct an inner geometry graph in order to keep track of relative transformations, however the behaviour of the graph no longer and its ability to describe relative transforms between any two primitives no longer made sense following the addition of group transformations. For example, it's not clear what `foo.align_left_of(bar)` would mean if both `foo` and `bar` were created using different transformation matrices, as they may no longer reside on the same plane to allow for any sort of alignment to occur. For the most part, these relative positioning methods are more useful for user interface, and still are available for widgets created with nannou's `ui` API today. Closes nannou-org#194.
This overhauls the `Draw` API with some pretty major changes. The largest of which, is that group transforms, blend modes and scissors are now possible via the new `draw.transform(mat4)`, `draw.blend(blend_desc)` and `draw.scissor(rect)` methods. Each of these methods return a new `Draw` instance of the same type, but with the given transform/blend/scissor applied! See [this comment](nannou-org#194 (comment)) for a short demo of what this looks like. None of the examples have been updated to take advantage of this yet, though there are many nature of code and generative design examples that have been awaiting these changes. In order to enable these changes, the `Draw` backend has overgone a major overhaul. Rather than rendering primitives when they are dropped, we now instead create an inner list of `DrawCommand`s. These are high-level commands that store minimal data and when combined, completely describe the users drawing. The `draw::Renderer` now digests a list of `DrawCommand`s, rather than taking the mesh directly. This allows to provide the primitives access to more useful state/caches/buffers during their rendering process. This also allows the `draw::Renderer` to generate a much more advanced render pass by creating unique `RenderPipeline`s per blend mode, unique `BindGroup`s per unique texture view (to-be-implemented) and switching between scissors during the render pass. These changes have also allowed for providing access to a glyph cache to the `Text` primitive. This means that, when `draw.text(str)` is used, glyphs associated with characters of text will now be cached on the GPU for efficient re-use. This means much faster drawing of large amounts of text than was previously possible. While this commit includes many additions, it also includes the removal of a significant number of positioning, sizing and orientation methods. Specifically, all methods that used to describe some relative transformation (e.g. "down_from", "align_right", etc) have been removed in favour of the new approach of using transformations. While these old relative positioning methods were occasionally useful, the new group transformations are strictly more flexible and provide a more familiar API to users coming from processing, openframeworks, etc. Previously, the **Draw** API used to construct an inner geometry graph in order to keep track of relative transformations, however the behaviour of the graph no longer and its ability to describe relative transforms between any two primitives no longer made sense following the addition of group transformations. For example, it's not clear what `foo.align_left_of(bar)` would mean if both `foo` and `bar` were created using different transformation matrices, as they may no longer reside on the same plane to allow for any sort of alignment to occur. For the most part, these relative positioning methods are more useful for user interface, and still are available for widgets created with nannou's `ui` API today. Closes nannou-org#194.
Problem
There's no easy way to translate+rotate a group of primitives that represent a single object.
In ProcessingJS for example you would be able to translate and rotate the coordinate system itself, then draw the primitives, then restore the state to what it was before. Right now you need to calculate the position+rotation of each primitive yourself.
Proposed solutions:
Canvas
primitive that can be drawn to, and then drawn using a position+rotation onto the real screenI am not completely familiar with this project so if I missed the intended way to do it, then I'd love to be pointed to docs of it. (Couldn't find any example of such a thing)
The text was updated successfully, but these errors were encountered: