Get new size of PShape after transform (shear)?

I’m transforming a shape with shearX. I’m looking for a way to get the new ‘real’ size of the shape after transforming. Is this possible? The shape is loaded from a SVG with loadShape.

I was thinking to do something like this:

  • Do transform
  • ‘Draw’ imaginary shape (to memory?)
  • Get width of this transformed shape
  • Use this new width to calculate the scaling I should do to make the transformed shale fit the screen

Base code looks like this:

PShape portal;
float easing = .1;
float posX, posY;

void setup(){
  size(1080, 1080);
  portal = loadShape("O.svg");
  portal.disableStyle();
  shapeMode(CENTER);
}

void draw(){
  float sx = map(mouseX, 0, width, 45, -45) - posX;
  posX += sx*easing;

  background(255);
  pushMatrix();
  translate(width/2, height/2);
  shearX(radians(posX));
// Here I would like to know the new width of this:
  shape(portal, 0, 0, 1325, height);
  popMatrix();
}

Already looked at a couple of ways, but not sure if they would work, or how to do them:
*Drawing controlpoints on a line transformed the same way and calculate the distance between them after transforming?
*Something like screenX for 2D transformations maybe?
*Drawing/adding the shape to a new shape and scale that one?

Thanks in advance!

1 Like

Third question on this forum, third time no replies. Starting to think there are stupid questions after all :wink:

Answering for people stumbling upon this question, wondering the same thing. I came a lot closer to a solution using the geomerative library by Richard Marxter. I set a transform matrix with shear and apply it to the shape. I can now compare the new width to the original width and scale accordingly before drawing the shape.

It doesn’t look like it’s 100% correct, not really sure why. Also have some trouble animating it nicely because the shear transform in Geomerative takes vectors instead of the ‘end situation’.

If someone has done something similar, or has some advice on how to do this correctly. Let me know.

1 Like

Hi @snaders – sorry that you haven’t found answers. This is a well-written question – not stupid at all – but sometimes SVG + loadShape is a very heavy lift to try to answer. In particular,

  1. the SVG spec is enormous
  2. Processing only supports a small subset of it
  3. without you providing a specific example SVG, it is very difficult to play around with your problem or do live testing (in part for the reasons above).

Do you want the shear of the bounding box of the svg, or the shear of the bounds of its (visible) elements? These can be different. Also, the first has a geometric solution – the second does not, and can get quite complex with nested child shapes etc.

If someone has done something similar, or has some advice on how to do this correctly. Let me know.

I’ve just done something similar for PText.

The initial problem is that Processing’s shearX() affects the translation matrix of a PShape only, and not the positions of the underlying vertices, so you can’t feasibly get the size/boundary of the new shape (at least to my knowledge).

Solution

Write a new shearX() method that does affect the PShape’s vertices, then simply find the enclosing rectangle of the new vertices.

shearX

Thankfully, it’s a fairly straightforward algorithm – for a given vertex, map it’s shear amount (x-displacement) based on it’s height from some Y position:

for (int i = 0; i < myPShape.getVertexCount(); i++) {
	PVector vertex = myPShape.getVertex(i);
	vertex.x += PApplet.map(vertex.y, 0, baselineY, 0, maxShearX);
	myPShape.setVertex(i, vertex); // need to call setVertex() to apply change
}

Then you can use these methods to find the most extreme points of the shape:

Code...
	/**
	 * Return value of smallest (up-most) Y coordinate from shape (may return
	 * negative values)
	 * 
	 * @param shape
	 * @return
	 */
	private static float getMinY(PShape shape) {
		float min = Float.MAX_VALUE;
		if (shape.getFamily() == GROUP) {
			for (PShape child : shape.getChildren()) { // search all children PShapes
				for (int i = 0; i < child.getVertexCount(); i++) {
					min = PApplet.min(child.getVertex(i).y, min);
				}
			}
		} else {
			for (int i = 0; i < shape.getVertexCount(); i++) { // search only parent PShape
				min = PApplet.min(shape.getVertex(i).y, min);

			}
		}
		return min;
	}

	/**
	 * Return value of greatest (down-most) Y coordinate from shape
	 * 
	 * @param shape
	 * @return
	 */
	private static float getMaxY(PShape shape) {
		float max = Float.NEGATIVE_INFINITY;
		if (shape.getFamily() == GROUP) {
			for (PShape child : shape.getChildren()) { // search all children PShapes
				for (int i = 0; i < child.getVertexCount(); i++) {
					max = PApplet.max(child.getVertex(i).y, max);
				}
			}
		} else {
			for (int i = 0; i < shape.getVertexCount(); i++) { // search only parent PShape
				max = PApplet.max(shape.getVertex(i).y, max);
			}
		}
		return max;
	}

	/**
	 * Return value of smallest (left-most) X coordinate from shape (may return
	 * negative values)
	 * 
	 * @param shape
	 * @return
	 */
	private static float getMinX(PShape shape) {
		float min = Float.MAX_VALUE;
		if (shape.getFamily() == GROUP) {
			for (PShape child : shape.getChildren()) { // search all children PShapes
				for (int i = 0; i < child.getVertexCount(); i++) {
					min = PApplet.min(child.getVertex(i).x, min);
				}
			}
		} else {
			for (int i = 0; i < shape.getVertexCount(); i++) { // search only parent PShape
				min = PApplet.min(shape.getVertex(i).x, min);

			}
		}
		return min;
	}

	/**
	 * Return value of greatest (right-most) X coordinate from shape
	 * 
	 * @param shape
	 * @return
	 */
	private static float getMaxX(PShape shape) {
		float max = Float.NEGATIVE_INFINITY;
		if (shape.getFamily() == GROUP) {
			for (PShape child : shape.getChildren()) { // search all children PShapes
				for (int i = 0; i < child.getVertexCount(); i++) {
					max = PApplet.max(child.getVertex(i).x, max);
				}
			}
		} else {
			for (int i = 0; i < shape.getVertexCount(); i++) { // search only parent PShape
				max = PApplet.max(shape.getVertex(i).x, max);
			}
		}
		return max;
	}

Processing’s native rotate() method is similar – again it affects the matrix and not the vertices of a PShape. After writing a rotate() method that affects a PShape’s vertices, we can similarly get bounding box as the shape is rotated:

rotate

rotate_string2

4 Likes