5

I am working with jQuery like elements from the cheerio library to manipulate SVG images.

These objects represent XML nodes and have a hierarchical internal structure.

I am writing a function (in JavaScript of course ) that visits each child node and, if it is a Basic Shape (ellipse, circle, rect...), calculates a set of points for the shape and invokes a user supplied function with the point set and element information.

Along the way, the function keeps track of all transform operations that have been specified upto that node, so that when it generates the point set for a shape, it will apply the accumulated scale, rotate, translate etc operations for that node.

The user supplied function will do things like generate polygon objects or calculate bounding boxes.

Is there a standard design pattern for this kind of function? It seems vaguely like a Visitor pattern but in that case the nodes would have a Visitor interface and invoke an operation themselves. I don't want to modify the cheerio objects. Furthermore, the nodes don't know where they are in the tree and cannot supply their transformation context.

This is not a difficult routine to write but I am interested to know if there is any formal theory that's relevant?

4
  • 2
    I don't want to modify the cheerio objects -- Then you can't use the Visitor pattern, unless you wrap it in another object that can hold your ongoing state operations, and then construct and return a brand new tree when you're done. Alternatively, you can write a recursive function that constructs a new tree from the existing one.CommentedMar 14, 2017 at 18:38
  • 1
    @RobertHarvey what about having the visitor emit an event containing the modified copy?CommentedMar 14, 2017 at 21:22
  • @RubberDuck: Sure, that will work. I don't know if you could still call that a Visitor, though.CommentedMar 14, 2017 at 21:26
  • Fair point. ANTLR has listeners, which sounds like where we're heading. Could be a useful example to reference.CommentedMar 14, 2017 at 22:16

1 Answer 1

1

For completeness, I thought I would post what I actually came up with:

//Traverses the SVG tree and invokes userFnc when ever it encounters //a Basic Shape node function shapesTraverser( jQsvgs, userFnc, transformers = Transformer() ){ jQsvgs.each( function(){ let jQ = $( this ); //parse the SVG 'transform' attribute and append it to //the sequence of tranformation operations transformers = transformers.cat( jQ.transform() ); if(isShapeElement( jQ ) ){ stop = userFnc( jQ, transformers ); } else{//recursive call shapes.shapesTraverser( jQ.children(), userFnc, transformers ) } } ); } //Rides on the shapeTraverser. For each Basic Shape, it calculates //a set of points, applies the transformation ops, and passes the //result to userFnc function pointsTraverser( jQsvgs, userFnc ){ let shapesFunc = function( jQ, transformers ){ let points = toPoints( jQ ); points = transformers( ...points ); userFnc( jQ, points ); } this.shapesTraverser( jQsvgs, shapesFunc ) } // Uses pointsTraverser to calculate a bounding box for the whole // SVG object function boundingBox( jQsvgs ){ let box = new BoundingBox(); function pointsFunc( jQ, points ){ box.merge( points ); } this.pointsTraverser( jQsvgs, pointsFunc ); return box; } 

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.