Brief description of problem: imagine having some vector picture(s) and text annotations on the sides outside of the picture(s).

Now the task is to scale the whole composition while preserving the aspect ratio in order to fit some view-port. The tricky part is that the text is not scalable only the picture(s). The distance between text and the image is still relative to the whole image, but the text size is always a constant.

Example: let's assume that our total composition is two times larger than a view-port. Then we can just scale it by 1/2. But because the text parts are a fixed font size, they will become larger than we expect and won't fit in the view-port.

One option I can think of is an iterative process where we repeatedly scale our composition until the delta between it and the view-port satisfies some precision. But this algorithm is quite costly as it involves working with the graphics and the image may be composed of a lot of components which will lead to a lot of matrix computations. What's more, this solution seems to be hard to debug, extend, etc.

I think you have to know which part of the x- and y-axis are constant and which are variable to calc the size upfront. Those parts that overlap with an Text are 1:1, the others 1:n
–
keuleJDec 6 '12 at 16:20

This problem is quite common in the CADD (Computer Aided Drawing and Design) industry where you have dimensions and texts that must scale in a different way than the rest of the drawing.

The industrial-strenght solution used in CAD systems is usually to have some kind of object hierarchy or object tree that represents the whole drawing. Each element of the drawing (included dimensions and texts) is represented in code and in memory by a different object (an instance of a class). Each object knows how to scale itself in the right way. (Of course, we are talking of vector graphics, not raster one)

This is a very heavy solution. It is both heavy to implement (it requires a scene-graph system that represent the drawing and a hierarchy of classes that represents the drawing elements) and to manage (a tree like this is large and heavy to be loaded and managed in memory).

In smaller systems, it is sometime possible to use two different layers. The first layer, on the background, contains the image. The second layer, on the foreground, is transparent and contains the unscalable parts (dimensions, text, etc.). The two layer are moved, rotated and scaled in synchro to each other but are still indipendent. This permits to scale and translate the main image and the dimensions/texts layer in different ways.

This second solution, however, still requires a layerable/stackable view system and quite a lot of code.

Usually, the people who need this kind of capabilities use some kind of geometrical kernel or geometrical library, like opencascade:

Ok, so I have some sort of answer that may be not ideal, but it works quite well.

Let's look at the next picture. A blue shape is scalable while the red one is not. Also note that vertex x scales with the rest of the image, only dimensions of the red shape are unscalable. For now on we'll look only at the X axis.

Now in this situation are two possible outcomes.

1: Scalable component scales over the end of unscalable one and so we have a standard situation where we scale a whole scalable composition:

2: If the bounds are small in size of even x vertex is located after the edge of the scalable component then problem with the scalable spears. But on the other hand we already know the size of the unscalable part, and we know that it will be positioned next to the bounds. This way we can just reduce size between the bounds by unscalable component's width and then scale the part of the whole composition without unscalable part.

This way we can compute both ratios for scaling and choose the one that results in a smaller final image.

Also in my implementation I have a current min/max X/Y values for each component. This values represent a current bounding box of the scalable part. Also components with unscalable parts have upwards, downwards, rightwards and leftwards unscalable offset. This way a true maximum x coordinate of this component will be maxX * scale + rightwardsOffset.

The algorithm is next one:

(for one axis)

Form two collections: one for positive offsets and one for negative.

Elements of collections should be pairs consisting of the value of unscalable offset and a coordinate of scalable boundary (in the above examples it will be an x as everything after it is considered to be unscalable).

Current bounds of the scalable part of the whole composition should be also added to this collections with 0 offsets (with is done to find a correct solution if no unscalable components are present or they are smaller and in the middle of the scalable once etc...)

For each pair of the elements from the negative and positive collections find a scale ratio that satisfies the given offsets. I calculate ratios as sizeToScale/sizeToFit.

Find the minimal ratio (or if you're calculating ratio the other way than me you should aim to get the smallest resulting image)

That's it. Remember, for other axis you calculate ratio the same way and take min.