paperjs / paper.js

The Swiss Army Knife of Vector Graphics Scripting – Scriptographer ported to JavaScript and the browser, using HTML5 Canvas. Created by @lehni & @puckey
http://paperjs.org
Other
14.43k stars 1.22k forks source link

Relative positioning of items is not working. #1317

Closed TimCabbage closed 7 years ago

TimCabbage commented 7 years ago

I've created a sketch that illustrates the issue:

http://sketch.paperjs.org/#S/rZHLasMwEEV/5eJNHDAJNISAS1ehXZd2GWchW2NL2B0ZeZo0Nf73SjZtoetu9JjHPdLcMWH1RkmevLYklUmypHI63rdbHD0pISh4qkRx0xEGo3rS6JUYXG1YrAwQ16OjWtA7ywIlBYfu8SPHYZ/hFrcJinVQGuwnwdUYr1aLWQoM2cbIXFXwRflF/QFMVzyH4+blG5+OBRcyU3KcYu9hf85iLOr+DYl3LR1d53yOVdmpql0VPK3vQ/IHc/ffnMYT8S8nMjZK66OxnU5n4pKo37kS6xiOn3xwIKULsawxk8P0HlVlUMdMBu8k+iCGltGUN+ygKaKGfH5ppCxV6S7qT8HHMrjXzn8Ykvx0nr4A

Basically the green rectangle should be at a position relative to the black one, 75 pixels to the right and down, and "orbiting" the original rectangle. It does not do so.

Since all items act like groups(have the addChild method), it is expected for the added children to be operating in the relative coordinates and inheriting the matrix transformation of the parent, relative to the parents pivot.

Is my thinking not correct? Thanks.

lehni commented 7 years ago

In Paper.js, only Group and Layer can have children.

You can use path.pivot to define to registration point to which the transformations related to, by default it is the center of its bounds.

You will probably also want to either use Shape items or set applyMatrix to false on Path items to prevent them from backing the transformations directly into their segments and store them in the path.matrix instead, see: https://groups.google.com/forum/#!searchin/paperjs/Introducing$20item.applyMatrix$20(was$3A$20item.transformContent)$20%7Csort:relevance/paperjs/4EIRSGzcaUI/seKoNT-PSpwJ

Shape items can be used for simple shapes that don't require an actual description of their geometry in bezier curves. They don't have actual content that the matrix could be baked into, and therefore applyMatrix is always false for them.

You can also toggle this setting globally through paper.settings.applyMatrix.

So your example could be rewritten like this:

Updated Sketch

See also #1272 for a discussion relating to item.pivot.

TimCabbage commented 7 years ago

Okay, it starts making sense with the applyMatrix = false; Basically as long as it's true, everything is created in world space and not in local space? Or am I wrong here?

lehni commented 7 years ago

Yes, as long as you're dealing with Path, CompoundPath, Group and Layer items. All other items (e.g. PointText, Raster, Shape, SymbolItem) can't bake the transformations in their content and store it in the matrix anyhow. Group, Layer and CompoundPath just pass it on to their children, which are their content (Paths only in case of CompoundPath). Path items store in their Segment list.

We decided to have applyMatrix true by default to keep things simple for beginners, so they don't need to deal with separate coordinate systems. But it comes to haunt us regularly : )

TimCabbage commented 7 years ago

Hum.

All the other systems I've worked with (Unity, Pixi and so on...) Do all positions, rotations, pivots and so on; in local coords. So it was very natural for me to expect the same. I would change it if I were You; the frustration of failing to get it working ( I spent now 7 days trying different approaches and it always broke ) would've made me drop paper.js if there was another library with the features I needed ( boolean operations + intersections )

It's just not intuitive, to be honest, that setting a position in the constructor and later via a method would yield different results.

You admittedly would probably need to change a large number of examples but hey, it's life. Also, set a pivot to local space [0, 0] by default - center of bounds is a very unintuitive way of thinking about the pivot point.

TimCabbage commented 7 years ago

oh. and if an item cannot have children, it should not allow you to call the method to add a child. Fail fast ;)

lehni commented 7 years ago

Systems are built for different purposes, and it sound like you come from those that are aimed primarily at gaming, so nested coordinate systems make a lot of sense.

The history of Paper.js goes back to Scriptographer, which lives inside Adobe Illustrator as a plugin. In Illustrator, there is only one coordinate system, and this actually made a lot of sense for our framework too, as it was aimed mainly at the creation of static, geometry and drawing based imagery through programmed, interactive tools. This is also what the initial focus of Paper.js was, and things have expanded from there since.

I agree that at this point, flipping the default to (applyMatirx = true) is a thing to consider, but it would break almost all all existing projects and scripts out there.

It's a step we can consider taking when releasing a major version update, and then communicate the change well, but first v1.0.0 needs to come out, without this breaking change.

If you spent 7 days tapping in the dark, where did you look for the information? I am aware that the site is too low on info on this, a question of not having enough time / manpower to work on docs / reference and the library as well at the same time... There needs to be a full tutorial on matrices and nested coordinate system. But there's always the mailing list, which is still the best place to ask such questions, to avoid losing so much time:

https://groups.google.com/forum/#!forum/paperjs

lehni commented 7 years ago

As for addChild() not complaining. The function exists on the Item base prototype for a flatter prototype hierarchy and hence better performance. This isn't ideal, of course, but a trade-off that we were willing to take when we designed the beginnings of the library years ago.

The reference says this about it:

http://paperjs.org/reference/item/#addchild-item

Returns: Item — the added item, or null if adding was not possible

We generally don't throw many exceptions. But this is probably a case where it would make sense.

TimCabbage commented 7 years ago

To be honest I never expected anything except a relative coordinate system for nested components especially since You do the matrix transformations and talk about transforming children and groups while maintaining relationships. It is obvious from that description that You'd use the relative coord system.

If You stated, on top of tutorials, in a big box "This system uses, by default an absolute coordinate system (adding something wit a position of 10,10 will put it in 10,10 of the viewport) To change that behaviour, do this:

So that adding a child with a position of 10,10 will add it at 10,10 relative to the parent element"

That would have solved all ambiguity.

Now for the addChild, You could state in the description that it applies only to the group and remove the methods from item docs. They would stay in, but people would know you need to use a group to have children.

Rest is in documentation, so no issues there. Great library BTW. Just annoying until you figure out the relative/absolute coord system.

lehni commented 7 years ago

Thanks! In Illustrator you have groups and layers, and yet they all share the global coordinate system. It really is a question of perspective and expectation here, I think. But I agree, this needs to be pointed out better...