how do I add custom properties?

298 views
Skip to first unread message

mo

unread,
Dec 5, 2012, 10:11:41 AM12/5/12
to pap...@googlegroups.com
Hi. Seems like a noob question, but didn't find a way yet how it works to add custom properties without touching the core code.
I've read that adding custom methods to class objects is done for example with "paper.PathItem.inject({myMethod:function(){...}});", using it by calling "paper.pathItem[0].myMethod()".

Here's what I'm trying to do:
I want to extend paper.Curve with the property "bounds", so whenever I call myPath.curves[0].bounds I get a rectangle object describing the boundaries of the specified straight line segment in myPath. As Method, it would look like this:
"paper.Curve.inject({bounds:function(){
        var l,t,w,h,c=this;
        l=Math.min(c.point1.x,c.point2.x);
        t=Math.min(c.point1.y,c.point2.y);
        w=(l==c.point1.x)?(c.point2.x-c.point1.x):(c.point1.x-c.point2.x);
        h=(t==c.point1.y)?(c.point2.y-c.point1.y):(c.point1.y-c.point2.y);
        return new Rectangle(l,t,w,h);
    }});"
How do I code that to get "bounds" as property (object) of paper.Curve? I also tried paper.Curve.define() and wrote the "get" function as the descriptor (whereas I don't need the setter, cause resulting rectangle should be calculated instead of changed manually), but couldn't get anything else than 'undefined' as returned statement...

Many thanks for help!

Jonathan Puckey

unread,
Dec 5, 2012, 10:39:40 AM12/5/12
to pap...@googlegroups.com
You need to use getBounds as a function name, it is converted internally to a getter:

paper.Curve.inject({
getBounds: function(){
var l,t,w,h,c=this;
l=Math.min(c.point1.x,c.point2.x);
t=Math.min(c.point1.y,c.point2.y);
w=(l==c.point1.x)?(c.point2.x-c.point1.x):(c.point1.x-c.point2.x);
h=(t==c.point1.y)?(c.point2.y-c.point1.y):(c.point1.y-c.point2.y);
return new Rectangle(l,t,w,h);
}
});

Jürg Lehni

unread,
Dec 5, 2012, 1:40:28 PM12/5/12
to pap...@googlegroups.com
Hi there,

There's an undocumented feature in Paper.js' underlying class inheritance sugar (bootstrap.js), which converts bean-style getter / setter functions to properties for you.

So when you inject a #getBounds() function, it turns into the getter for a #bounds property (the getter function is still there too, but we don't document these in the API).

The bounds function you are planning to add is the controlBounds, right? So I suggest you name it the same way as in the Path class, where it already exists. #bounds there are the tight bounds of a path.

But you are right, these are missing from Curve currently. I shall look into adding those there, and perhaps change the code in Path to use them instead of replicating functionality.

Jèrg

Jürg Lehni

unread,
Dec 5, 2012, 1:41:21 PM12/5/12
to pap...@googlegroups.com
Oh, sorry, I've missed that it was answered already!

mo

unread,
Dec 5, 2012, 2:58:11 PM12/5/12
to pap...@googlegroups.com
Wow, pretty fast answer, thanks a lot. Makes sense, since I stumbled upon this in the Base.extend context somewhere in the source code, where I was wondering about.
Another question (though it needs a separate topic):
Is it possible to extend the paper.path class with an instance such as subpath? I need that for my custom path intersection/geometric boolean operation handling.

mo

unread,
Dec 5, 2012, 3:10:28 PM12/5/12
to pap...@googlegroups.com
Hi Jürg, your explanation is very welcome! I needed the tight boundaries of a straight curve, instead of taking the handles into account. In the case of computing the boundaries of bezier curves I've written a function to get the "Convex Hull", which I need for searching for intersections between curves.

Jürg Lehni

unread,
Dec 5, 2012, 4:19:47 PM12/5/12
to pap...@googlegroups.com
If you're implementing boolean operations, why not just adding them to Path?

This is on our list of things to add, so perhaps we should plan it a little and work together?

As for subclassing, that's not something that is supported at the moment. It might work, it might not, it might work in most cases, but not in very rare cases that are hard to pinpoint, it might need only a couple of changes to make it work well, but those might be in many different places.

For example, each Item subclass has a _type property which is a string representing its type. Sometimes we check that instead of using instanceof, because it's faster, and so far, for example for Path we just assumed there would be no subclassing.

Jürg

Jürg Lehni

unread,
Dec 5, 2012, 4:22:06 PM12/5/12
to pap...@googlegroups.com
It seems you are replicating many things that are already in the library. Path can already calculate the convex hull for you, using derivatives of the x- and y- bezier functions. And if I move that from path to Curve, the two things can merge (and they should), reducing memory footprint a lot.

mo

unread,
Dec 5, 2012, 6:26:53 PM12/5/12
to pap...@googlegroups.com
Hi Jürg,
I understand that subclassing should never be first choice, and I love paper.js for its simplicity. The reason why it probably would be useful to have a path subdivided into subpaths (after a boolean operation or path intersection) is, the subpaths can be merged or deleted, shaping new vector objects. For example apply a "join" operation on two circles: both intersect each other in two points, so you have two paths (the circle outlines) with each having two subpaths then. Delete those which overlap the opposite shape/path, and connect the remaining ones. Done. For "intersection" operation the subpaths within the intersection area remain. But it's possible to do a lot more stuff with subpaths, for example deleting 'virtual' overlaps of a self intersecting path, like in a drawn knot, where one slope overlaps another. Having this feature in a vector drawing app is awesome useful.

Anyway, you're right: planning is everything and I'd love to contribute to this project. I'm not a good coder, but have more than 17 years practical experience with vector drawing applications and plugin scripting (entirely OOP) related to those...

mo

unread,
Dec 5, 2012, 6:40:22 PM12/5/12
to pap...@googlegroups.com
Lol, I thought of that. It still takes some efforts for me to learn the core code of paper.js, but I was rather testing some intersection algorithms for their efficiency, instead of searching for given implementations. I tried the "Curve Flattening" along with "Divide and Conquer" approach but it's neither reliable nor efficient nor exact enough. My next try was to apply the "Fat Line Clipping" approach.
I wonder how many steps I already replicated, which have been tested, implemented or omitted in your development...

Jürg Lehni

unread,
Dec 6, 2012, 12:41:01 PM12/6/12
to pap...@googlegroups.com
Fat line clipping is what i looked into as well, and divide and conquer is what is currently in use in Scriptographer. We should probably start by porting that over, which should be trivial, then improve from there. Even if it's not super precise, it will be a good start to implement boolean operations on top, and can be swapped with a working fat line implementation later on. Hopefully I will soon find some time to start working on this. Things are rather busy right now though...

As for the path sub-classing, if that's just needed to mark a path as a sub-path, we could also just set a property on paths created in this way. I don't think a sub-class is required just for that. But the thought is good!

BTW, did you see this bezier curve primer by Mike "Pomax" Kamermans? It's an extremely valuable resource:

http://processingjs.nihongoresources.com/bezierinfo/

He also started covering boolean shape operations here:

http://processingjs.nihongoresources.com/shapeboolean/

Best,

Jürg

mo

unread,
Dec 6, 2012, 2:20:35 PM12/6/12
to pap...@googlegroups.com
Hi Jürg,
There's actually NO absolute exact algorithm for bezier intersection, and some approaches (Curve Flattening) are executed fast with low level maths while being very inaccurate, and there are more complex approaches which turn out to be more precise (like Fat Line Clipping). The latter one needs less iterations, so it might be the best choice (in terms of speed and memory usage) for subdividing curves at intersections.
Mr. Kamermanns great Article has become my bedtime reading since two weeks now! I found some similar great educational resources, which cover the special cases (multiple and tangential intersections) more in detail. I'm close to get the math into working javascript code I think.
As for the subpaths: my thoughts last night were mostly the same - adding the subpath as property to the path would keep the object model clean and subclassing doesn't make sense when the subpath inherits the same methods and properties as the path anyway. The subpath would even be sufficient as private property...

Jürg Lehni

unread,
Dec 6, 2012, 2:42:10 PM12/6/12
to pap...@googlegroups.com
Yes, I am well aware of the particularities of bezier curves. Tackling the math behind them has been one of the things I have enjoyed the most while working on Paper.js.

It sounds like your expertise in the field is very advanced, so wether you're good at JavaScript or not shouldn't matter much. I'm happy to help out, clean up code and point you in the right directions.

Best,

Jürg

mo

unread,
Dec 6, 2012, 2:49:12 PM12/6/12
to pap...@googlegroups.com
You wrote that Path can already calculate the Convex Hull...Where do I find it in the code, respectively in which context? I searched in the boundary section and elsewhere, but haven't figured it out yet...
Once I have it I probably could use/alternate it to define the Fat Line of a paths curve, the rest should be the easiest part I guess.
Is there something like the "de Casteljau's" algorithm already implemented as well? I have a snippet for bezier curve splitting, but maybe this is redundant too... ;-)

Jürg Lehni

unread,
Dec 6, 2012, 4:48:29 PM12/6/12
to pap...@googlegroups.com
It's at the end of Path.js, the part that calculates bounds. Look for the private getBounds(matrix, strokePadding) function.

It's a little tricky to isolate though, since it's written to handle many different cases while being efficient with amount of lines of code.

I'd like to look into how to move this to Curve somehow. But for now, you could just work around that by building a new Path for the curve you want the bounding box for, get the bounds, destroy the path. It wouldn't be so fast, but it wouldn't force you to rip things apart either.

I'll look into how to streamline it all.

J
Reply all
Reply to author
Forward
0 new messages