Jump to content
This forum will be closing down. Please move to the respective dedicated project forums.

Pixi.js: Optimize panzooming scalable graphics


Daawaan4U
 Share

Recommended Posts

I'm currently learning PixiJS to replace my SVG-based visualizer with WebGL for performance reasons. My current prototype can be found at https://stackblitz.com/edit/pixi-panzoom. The objective is to display a static polyline with a chain of arrows while allowing the user to pan and zoom to the figure without affecting the width of the stroke of the lines or the size of the arrow head so manipulating the transform matrix of the parent container is not an option. The number of arrows that has to be displayed can reach up to 10,000 to 20,000 so using a `Graphics` instance to draw the shape primitives can get expensive. In addition, I have to render the lines (the body of the arrows) with a round cap.

Currently, my approach is to render the primitives (parts of the arrow) into a `Texture` first. The tail of the arrow is a circle to give the impression that the line has a round cap. The body of the arrow simply uses the out of the box `WHITE` texture. The head of the arrow is ... well a polygon forming an arrow head.

const arrowTailGraphics = new Graphics();
arrowTailGraphics.beginFill(0xffffff);
arrowTailGraphics.drawCircle(1.5, 1.5, 1.5);

const arrowHeadGraphics = new Graphics();
arrowHeadGraphics.beginFill(0xffffff);
arrowHeadGraphics.drawPolygon([
  new Point(0, 0),
  new Point(24, 12),
  new Point(0, 24),
  new Point(6, 12),
]);

const arrowBodyTexture = Texture.WHITE;
const arrowTailTexture = app.renderer.generateTexture(arrowTailGraphics, {
  multisample: MSAA_QUALITY.HIGH,
  resolution: 2,
  scaleMode: SCALE_MODES.LINEAR,
});
const arrowHeadTexture = app.renderer.generateTexture(arrowHeadGraphics, {
  multisample: MSAA_QUALITY.HIGH,
  resolution: 2,
  scaleMode: SCALE_MODES.LINEAR,
});

Afterwards, sprites are created from each texture then added into a `ParticleContainer` for the corresponding texture.

const createArrowBodySprite = () => {
  const sprite = new Sprite(arrowBodyTexture);
  sprite.anchor.set(0, 0.5);
  return sprite;
};

const createArrowTailSprite = () => {
  const sprite = new Sprite(arrowTailTexture);
  sprite.anchor.set(0.5, 0.5);
  return sprite;
};

const createArrowHeadSprite = () => {
  const sprite = new Sprite(arrowHeadTexture);
  sprite.anchor.set(1, 0.5);
  return sprite;
};

const arrowBodiesContainer = new ParticleContainer();
arrowBodiesContainer.autoResize = true;
arrowBodiesContainer.setProperties({
  scale: true,
});
const arrowTailsContainer = new ParticleContainer();
arrowTailsContainer.autoResize = true;
const arrowHeadsContainer = new ParticleContainer();
arrowHeadsContainer.autoResize = true;
for (let index = 0; index < count - 1; index++) {
  arrowBodiesContainer.addChild(createArrowBodySprite());
  arrowTailsContainer.addChild(createArrowTailSprite());
  arrowHeadsContainer.addChild(createArrowHeadSprite());
}

To make the figure responsive with panning and zooming, the transform matrix of each sprite is modified with the scale and translate for every frame. `panzoomView` is a simple custom class I wrote to deal with panning and zooming in PixiJS 

const scale = panzoomView.scale.current;
const translate = panzoomView.translate.current;

for (let index = 0; index < count - 1; index++) {
  const arrowBodySprite = arrowBodiesContainer.children[index] as Sprite;
  arrowBodySprite.position.x = pointsX[index] * scale + translate.x;
  arrowBodySprite.position.y = pointsY[index] * scale + translate.y;
  arrowBodySprite.width = magnitude[index] * scale;
  arrowBodySprite.height = 4;
  arrowBodySprite.rotation = angle[index];

  const arrowTailSprite = arrowTailsContainer.children[index];
  arrowTailSprite.position.x = pointsX[index] * scale + translate.x;
  arrowTailSprite.position.y = pointsY[index] * scale + translate.y;

  const arrowHeadSprite = arrowHeadsContainer.children[index];
  arrowHeadSprite.position.x = pointsX[index] * scale + translate.x;
  arrowHeadSprite.position.y = pointsY[index] * scale + translate.y;
  arrowHeadSprite.rotation = angle[index] + Math.PI;
}

At 1000 arrows, panning and zooming is consistently at 60FPS. At 10,000 arrows however, lag is noticeable but panning and zooming is still useable. Are there any other rendering techniques or optimizations I can use to maintain a consistent 60FPS when dealing with atleast 10,000 arrows?

Link to comment
Share on other sites

  • 2 weeks later...

I'm sorry, but i currently do not have capacity yo answer "10000 bunnies" questions on forums, because it requires serious explanation.

Moving anything from SVG is usually a pain, if you dont know how WebGL works and dont have experience in its optimization.

handling 10k objects in real task is a sign that you are programmer and not a neural network, so yeah, prepare for the bumpy ride.

Please ask this question in our discord.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...