Is there a recommended way to extend classes in Paper.js? In particular, I am interested in extending Path
Pardon if my terminology is incorrect, but I am essentailly asking the same question about paper that is being asked about three here
Based on your comment to the initial version of my answer, you are looking for the 'extend' function (oops, that was exactly what you meant) to do subclassing. In an email to the paper.js mailing list, Jürg Lehni (one of the creators) said:
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.
A complication is that there are no paper.Path.Rectangle objects. There are paths, and there are rectangles, but when you call new paper.Path.Rectangle() it creates a new Path using initialization code (createRectangle) that creates a rectangular shape.
So we would need to extend paper.Path. Unfortunately, when you call new paper.Path.Rectangle it calls createPath, which always returns a Path (not your extension). It may be possible to do something like:
var SuperRectangle = paper.Path.extend({
otherFunc: function() {
console.log('dat');
}
});
...and with correctly substituting/overriding for createRectangle or createPath get a subclass to work. Unfortunately, I have not been able to manage it.
My first working recommendation is to make a factory and add your functions to the objects in that factory (jsbin here):
var createSuperRectangle = function(arguments){
var superRect = new paper.Path.Rectangle(arguments);
superRect.otherFunc = function(){
console.log('dat');
}
return superRect;
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = createSuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
Similarly, you can use the factory to just change the prototype for your SuperRectangles, having added your functions to that prototype object (and making its prototype the one from paper.Path.__proto__) (jsbin here):
var superRectProto = function(){};
var tempRect = new paper.Path.Rectangle();
tempRect.remove();
superRectProto.__proto__ = tempRect.__proto__;
superRectProto.otherFunc = function(){
console.log('dat');
}
delete tempRect;
var createSuperRectangle = function(arguments){
var superRect = new paper.Path.Rectangle(arguments);
superRect.__proto__ = superRectProto;
return superRect;
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = createSuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
Alternatively, you can make an object that encapsulates the Path (jsbin here):
var SuperRectangle = function(arguments){
this.theRect = new paper.Path.Rectangle(arguments);
this.otherFunc = function(){
console.log('dat');
}
}
var aRect = new Rectangle(20, 30, 10, 15);
var aPath = new SuperRectangle({
rectangle: aRect,
strokeColor: 'black'
});
aPath.otherFunc();
aPath.theRect.strokeWidth = 5;
Unfortunately, then to access the path you have to use the theRect variable.
Initial incorrect answer follows:
I don't think you mean "extending classes". In Javascript you can extend objects so that they have more functions, so extending the Path "class" would mean all Path objects have the same new functions. Javascript object extension is further described here.
If I'm wrong, and you do want to extend Path, then you can use:
paper.Path.inject({
yourFunctionName: function(anyArgumentsHere) {
// your function here
}
});
However, I think you are actually talking about creating new objects that mostly behave like Path objects but have different functionality from each other. If that is the case, then you may want to look at this answer about Javascript using prototypical inheritance. For example, here I create two Rectangle objects that behave differently when I ask them to doSomething (jsbin here):
var rect1 = new Path.Rectangle({
point: [0, 10],
size: [100, 100],
strokeColor: 'black'
});
rect1.doSomething = function() {
this.fillColor = new Color('red');
};
var rect2 = new Path.Rectangle({
point: [150, 10],
size: [100, 100],
strokeColor: 'black'
});
rect2.doSomething = function() {
this.strokeWidth *= 10;
};
rect1.doSomething();
rect2.doSomething();
A couple of things.
1) You can wrap the original paperjs object but this is very much a hack
paperjs playground
function SuperSquare() {
this.square = new Path.Rectangle({
size:50,
fillColor:'red',
onFrame:function(base) {
var w2 = paper.view.element.width / 2;
this.position.x = Math.sin(base.time) * w2 + w2;
}
});
}
SuperSquare.prototype.setFillColor = function(str) {
this.square.fillColor = str;
}
var ss = new SuperSquare();
ss.setFillColor('blue');
2) I may clone & create a paper 2017 which operates off of es6 so that you can use the extend keyword.
3) I wrote an application called Flavas but it never gained a following so I just kind of left it. That being said, I have been playing with it lately; upgrading it to es6. With it you can do what you're talking about.
class Square extends com.flanvas.display.DisplayObject {
constructor(size) {
this.graphics.moveTo(this.x, this.y);
this.graphics.lineTo(this.x + size, this.y);
this.graphics.lineTo(this.x + size, this.y + size);
this.graphics.lineTo(this.x, this.y + size);
}
someMethod(param) {
trace("do something else", param);
}
);
I wrote all this kind of quick so feel free to hit me up with Q's.
Related
As an example: Here are two classes, Entity and Character. Entity makes an object on a game map, and it has rest parameters to set where you can interact with it.
class Entity {
constructor(sprite, pos, func, ...intcoords) {
this.sprite = sprite;
this.func = func;
move(pos, ...intcoords);
}
move(pos, ...intcoords) {
this.y = pos[0]; this.x = pos[1];
this.sprite = drawImg(this.sprite, this.y, this.x);
for (let i in intcoords) {
// process coordinates that should execute this.func: [y, x], [y, x]...
}
}
}
Meanwhile, Character makes an object that is able to talk with a pre-defined template.
class Character {
constructor(name, talkimg = name + "webp", talkpos = [70, 20], talkflip = false) {
this.name = name; this.talkimg = talkimg;
this.talkpos = talkpos; this.talkflip = talkflip;
}
async talk(...strings) {
speechSet(this.name, this.talkimg, this.talkpos, this.talkflip);
await speech(...strings);
}
}
In reality, characters will be activated because of a matching entity. So I believe the cleanest and most logical thing to do is have Character extend Entity. That way, I could have a single object like ben that I could call both ben.move() and ben.talk() with.
However, this would require using 7 parameters for Character, and sacrificing either default or rest parameters, which sucks. I could do it, and it would work, but it would suck and I would hate it. Ideally, I could define these parameters separately while still having a single object. I believe the best way would be if I could make an Entity separately, and then make that instance the super of a new Character.
One idea I had was making a single parameter at the beginning of Character, which could be passed an instance of Entity, and I could use that when declaring super(). That would allow me to define it this way and save it to a single variable:
ben = new Character(
new Entity("ben.webp", [10, 6], benEvent, [9, 6], [9, 5]),
"Ben", "ben-angry.webp", [60, 30]);
The problem is that this creates and draws a new Entity, and then a duplicate is created inside Character. That's not going to work. Even if it didn't do that, Entity would have to save every parameter passed into it as a unique variable (which isn't used for any other purpose), then Character must reference those individually. That doesn't suck as bad, but I'd still prefer if I didn't do that.
Is it possible to create a single gravity / force point in matter.js that is at the center of x/y coordinates?
I have managed to do it with d3.js but wanted to enquire about matter.js as it has the ability to use multiple polyshapes.
http://bl.ocks.org/mbostock/1021841
The illustrious answer has arisen:
not sure if there is any interest in this. I'm a fan of what you have created. In my latest project, I used matter-js but I needed elements to gravitate to a specific point, rather than into a general direction. That was very easily accomplished. I was wondering if you are interested in that feature as well, it would not break anything.
All one has to do is setting engine.world.gravity.isPoint = true and then the gravity vector is used as point, rather than a direction. One might set:
engine.world.gravity.x = 355;
engine.world.gravity.y = 125;
engine.world.gravity.isPoint = true;
and all objects will gravitate to that point.
If this is not within the scope of this engine, I understand. Either way, thanks for the great work.
You can do this with the matter-attractors plugin. Here's their basic example:
Matter.use(
'matter-attractors' // PLUGIN_NAME
);
var Engine = Matter.Engine,
Events = Matter.Events,
Runner = Matter.Runner,
Render = Matter.Render,
World = Matter.World,
Body = Matter.Body,
Mouse = Matter.Mouse,
Common = Matter.Common,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create();
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: Math.min(document.documentElement.clientWidth, 1024),
height: Math.min(document.documentElement.clientHeight, 1024),
wireframes: false
}
});
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
Render.run(render);
// create demo scene
var world = engine.world;
world.gravity.scale = 0;
// create a body with an attractor
var attractiveBody = Bodies.circle(
render.options.width / 2,
render.options.height / 2,
50,
{
isStatic: true,
// example of an attractor function that
// returns a force vector that applies to bodyB
plugin: {
attractors: [
function(bodyA, bodyB) {
return {
x: (bodyA.position.x - bodyB.position.x) * 1e-6,
y: (bodyA.position.y - bodyB.position.y) * 1e-6,
};
}
]
}
});
World.add(world, attractiveBody);
// add some bodies that to be attracted
for (var i = 0; i < 150; i += 1) {
var body = Bodies.polygon(
Common.random(0, render.options.width),
Common.random(0, render.options.height),
Common.random(1, 5),
Common.random() > 0.9 ? Common.random(15, 25) : Common.random(5, 10)
);
World.add(world, body);
}
// add mouse control
var mouse = Mouse.create(render.canvas);
Events.on(engine, 'afterUpdate', function() {
if (!mouse.position.x) {
return;
}
// smoothly move the attractor body towards the mouse
Body.translate(attractiveBody, {
x: (mouse.position.x - attractiveBody.position.x) * 0.25,
y: (mouse.position.y - attractiveBody.position.y) * 0.25
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/matter-attractors#0.1.6/build/matter-attractors.min.js"></script>
Historical note: the "gravity point" functionality was proposed as a feature in MJS as PR #132 but it was closed, with the author of MJS (liabru) offering the matter-attractors plugin as an alternate. At the time of writing, this answer misleadingly seems to indicate that functionality from the PR was in fact merged.
Unfortunately, the attractors library is 6 years outdated at the time of writing and raises a warning when using a newer version of MJS than 0.12.0. From discussion in issue #11, it sounds like it's OK to ignore the warning and use this plugin with, for example, 0.18.0. Here's the warning:
matter-js: Plugin.use: matter-attractors#0.1.4 is for matter-js#^0.12.0 but installed on matter-js#0.18.0.
Behavior seemed fine on cursory glance, but I'll keep 0.12.0 in the above example to silence it anyway. If you do update to a recent version, note that Matter.World is deprecated and should be replaced with Matter.Composite and engine.gravity.
is it possible to pass a value from openjscad to javascript?
I would like to show the dimensions of a 3d-object in a div or any other html-element.
Example:
A cube is created with openjscad with the following parameters:
function main() {
var cube = CSG.cube({
center: [0, 0, 0],
radius: [1, 2, 3]
});
return cube;
}
How can I pass the parameters for center and radius to javascript in order to use them in other areas of the website?
I did this by making my own cube object, you can add all the variables you want and add some functions too like my translate function.
function myCube(size,pos){
this.obj = cube({size:size,center:false}).translate(pos);
this.size = size;
this.pos = pos;
this.translate = function(pos){
this.obj.translate(pos);
};
}
then you can make a cube like this var MC = new myCube(radius, [x,y,z]);
and now to get your radius you can do MC.size;
it does get tricky when you try to do something with the shape itself, you'll have to do something like union(MC.obj, CIL.obj)
ps: I hope this helps although I know this isn't the ideal way and there's probably some easy way of doing it but this worked for me.
I am Currently Trying to work with JavaScript in a cleaner object orientated way, So Please excuse me if I'm doing this entirely incorrectly I am using this previous questions answer as a general reference, but Here's my 'test' code:
//Create some sample objects to play with.
var testJSON = {
"rectangle": [
{ "id":3 , "x":5, "y":10, "width":10, "height":50
}
]
};
//Create Rectangle Constructor
var rectangle = {
init: function( i, x, y, width, height ) {
this.id = i,
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.fields = []
},
move: function( x, y ) {
this.x += x;
this.y += y;
}
};
//Create test array to hold all the objects
var test = [];
//Create a new rectangle object
var myRectangle = Object.create( rectangle );
myRectangle.init( 1, 0, 0, 2, 4 );
myRectangle.move( 3, 5 );
//put rectangle object in array associated with id
test[myRectangle.id] = myRectangle;
//Create a new rectangle object with the same variable name as it will all be in an array anyway.
var myRectangle = Object.create( rectangle );
myRectangle.init( 2, 0, 0, 2, 4 );
myRectangle.move( 0, 0 );
//put rectangle object in array associated with id
test[myRectangle.id] = myRectangle;
//put JSON result in
test[testJSON.rectangle[0].id] = testJSON.rectangle[0];
//No Longer need this variable, is it worth getting rid of.. i dont know
myRectangle =null;
//Try and use methods created in the constructor.
test[2].move(4,8);
console.log(test);
Okay, Now the actual questions, The Application I am trying to create, has both json data and it will have users that create data, so for example: the application will generate a bunch of 'rectangles' and then the user can also create rectangles.
So the first question would be, 'Is this the correct approach' and then secondly how would i get the json data to also have the method defined in the rectangle constructor (move)?
Any Help Greatly Appreciated.
This an extension of what #Marc B and #Ray Toal mentioned...
You have created javascript functions - which is not at all that bad - but not JSON. To create graphics, you could use the canvas tag as, in effect, a "drawing div" and use the object's x and y positions to constantly update them - if you were considering doing 2d animation. If so, I would recommend CreateJs as a starting block before hard-coding JAVASCRIPT animations. Again, you're code is just fine, but having users create this data - if you were considering a server application would involve using real JSON or perhaps Node.js - but for now I'd focus on single player aspect and setting up you're objects on a graphical 'interface' like <canvas>
I am working with box2dweb and i am trying to make a function, that would add instructions how to draw a 'body' based on the 'body' shape.
That is: When received a 'b2BodyDef' get the shape, and with external information, get the shape specifications. To do this i need to cast 'b2Shape' back to 'b2CircleShape'.
I guess with C++ this would be something like
b2CircleShape* shape_circle = dynamic_cast< (b2CircleShape*) >( shape );
How do i do similar thing with javascript? I do know there are tons of other ways to do this (like pass the wanted radius on this example as parameter) but i would like to do what i feel like right and not a hack.
function Add_new_drawable_object_to_world( body, type )
{
GLOBAL_world_objects.push( body );
var s = new Sprite();
if ( type == OBJECT_TYPE_PLAYER )
{
s.graphics.beginFill ( 0x2222ff, 0.6);
var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
var fixture_list = body.GetFixtureList();
var shape = fixture_list.GetShape() ;
// FIXME: TypeError: shape.GetRadius is not a function
var radius = shape.GetRadius();
// here i would draw fancy circle with 'radius'
And earlier i have:
// Create player
var player = new b2FixtureDef(); // ball fixture definition
player.shape = new b2CircleShape();
player.density = 0.5;
player.shape.SetRadius( 0.2 );
var bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set( 0.0, 0.0 );
var body = GLOBAL_world.CreateBody(bodyDef);
body.CreateFixture( player );
Add_new_drawable_object_to_world( body, OBJECT_TYPE_PLAYER );
As Bergi says, Javascript has no classes so the concept of private/protected members does not really exist. I would suggest having a look at how box2dweb does this, because the debug draw display does almost the exact same thing you are doing here. Search for b2World.prototype.DrawShape in the box2dweb source.
If it makes you feel better, in the original C++ b2CircleShape the member variables are public, so there is no GetRadius and the 'internals' are accessed directly :)