Size JointJS Rect shape based on text? - jointjs

Usually, when we create a Rect shape in JointJS, we specify a size.
var cell = new joint.shapes.basic.Rect({
size: size,
attrs: {
text: {
text: text
}
}
});
graph.addCell(cell);
Is there a way to not provide a size property, but have the shape take on the dimensions required to fit the text?
I am aware of joint.util.breaktext, but I do not wish to break text in this case.

Related

SwiftUI, How to set Text width correctly?

I have a very simple ContentView, The Text's width set to 68
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
.frame(width: 68)
}
.padding()
}
}
But in Debug View Hierarchy I can see the Text's width is not 68 at all, instead, the wrapper width of the Textis 68. Did I miss something?
Text isn't greedy by default and won't take all the space it's got available to it unless it's needed. Due to the extra width not being needed, it won't use it.
The width of the VStack is 68 as it's sized based on the size of it's child views, in this case it's only child view is explicitly saying it's width is 68, so the width of the VSTack will be 68 and since the Text isn't greedy in this case, it'll only take up the horizontal space that it needs. This leads to the width of the Text being less than the width of the VSTack.
If you don't explicitly set the width of the Text you will see that the width of the VStack will match the width of the view it contains.
The frame is 68 wide but the Text inside it doesn't need that much so there is a gap. View modifiers usually wrap what's inside in an outer view, e.g.
myView.frame() is essentially doing:
FrameView {
myView
}
If you want bigger text you could increase the font size ;-)

Fit text to existing Surface in pygame

I'm trying to create text surfaces that are of the same size no matter the text. In other words: I want longer text to have smaller font size and shorter text to have bigger font size in order to fit the text to an already existing Surface.
To create text in pygame I am:
Creating a font object. For example: font = pygame.font.SysFont('Arial', 32)
Creating a text surface from the font object. For example: text = font.render('My text', True, (255, 255, 255))
Bliting the text surface.
The problem is that I first need to create a font object of a certain size before creating the text surface. I've created a function that does what I want:
import pygame
def get_text(surface, text, color=(255, 255, 255), max_size=128, font_name='Arial'):
"""
Returns a text surface that fits inside given surface. The text
will have a font size of 'max_size' or less.
"""
surface_width, surface_height = surface.get_size()
lower, upper = 0, max_size
while True:
font = pygame.font.SysFont(font_name, max_size)
font_width, font_height = font.size(text)
if upper - lower <= 1:
return font.render(text, True, color)
elif max_size < 1:
raise ValueError("Text can't fit in the given surface.")
elif font_width > surface_width or font_height > surface_height:
upper = max_size
max_size = (lower + upper) // 2
elif font_width < surface_width or font_height < surface_height:
lower = max_size
max_size = (lower + upper) // 2
else:
return font.render(text, True, color)
Is there any other way to solve this problem that's cleaner and/or more efficient?
This, unfortunately, seems to be the most appropriate solution. Font sizes are more of a approximation and differs between fonts, so there isn't a uniform way to calculate the area a specific font will take up. Another problem is that certain characters differs in size for certain fonts.
Having a monospace font would theoretically make it more efficient to calculate. Just by dividing the surface's width with the string's length and check which size of a monospace font covers that area.
You can re-scale the text image to fit:
def fit_text_to_width(text, color, pixels, font_face = None):
font = pygame.font.SysFont(font_face, pixels *3 // len(text) )
text_surface = font.render(text, True, color)
size = text_surface.get_size()
size = ( pixels, int(size[1] * pixels / size[0]) )
return pygame.transform.scale(text_surface, size)
The Font object seems to have to methods size() -> (width, height) and metrics() -> list[(minx, maxx, miny, maxy)] that sounds useful for checking the size of each resulting character and width of text.
The simplest way would be to use size and simply scale down text to the required width and height based on some ratio screen_width / font.size()[0] for example.
For breaking the text into lines, the metrics method is needed. It should be possible to loop through the metrics list and split up the text based on the summed width for each line.

How to isolate a Phaser shader to a specific object/shape?

I'm using the Phaser framework. Here is the jsfiddle:
http://jsfiddle.net/Dillybob/u3mGL/13/
Here is where the filter is getting populated:
background = game.add.sprite(0, 0);
background.width = 800;
background.height = 600;
filter = game.add.filter('Fire', 800, 600);
filter.alpha = 0.0;
background.filters = [filter];
My line object is assigned to the variable drawnObject
So I assign that object to receive the filter like so:
drawnObject.filters = [filter];
But my line is now a red fiery square instead of being a line with a fiery background, why?
Firstly, be aware that drawnObject is actually a bitmap, which is rectangular shaped. It consists of white pixels, which build your line, and transparent pixels, which are taking the rest of bitmap space.
The filter you use is a pixel shader. Pixel shader describes instructions that GPU invokes for each pixel of a provided bitmap. In case of this shader, it creates fire effect based on some noise functions, but it doesn't take original bitmap into account. The original color of pixels is not preserved, it doesn't add to final effect in any way.
To achieve your expected result, you have to amend fragmentSrc in Fire.js, so that shader uses and mixes/blends original color into final pixel color and/or doesn't change pixel transparency.

geoxml3: Syntax for markerOptions{shape}

I want to limit the clickable area of all the icons from a KML file, and I'm a little stumped about how to make that happen. The icons are all the same typical-style pointer, and I'd like to limit the clickable area to the circle encompassed by the top of the pointer. The icon is 19x32, so I think I want a circle centered 9px from the top, 9px from the left, with a radius of 9px. If geoxml3 will do that, I figure that would be specified in the parser object, though maybe it would be in the IconStyle in the KML file. If in fact that would be in the parser object I haven't found the right syntax. It is apparently not:
var blues = new geoXML3.parser({map: map, singleInfoWindow: true, zoom: false, markerOptions: {shape: {type:circle, coords: [9px,9px,9px]}}});
blues.parse('allbluesdance.kml');
The markerOptions option to GeoXml3 is exactly a Google Maps Javascript API v3 markerOptions object.
Your icon is 49x32 pixels, the center of the circle is defined from the top left, so you probably want 24,9 for the center and a radius of 9:
var blues = new geoXML3.parser({
map: map,
singleInfoWindow: true,
suppressDirections: true,
markerOptions: {
shape: {
type: 'circle',
coords: [24,9,9]
}
},
zoom: false
});
From the documentation on Complex Icons:
// Shapes define the clickable region of the icon.
// The type defines an HTML <area> element 'poly' which
// traces out a polygon as a series of X,Y points. The final
// coordinate closes the poly by connecting to the first
// coordinate.
var shape = {
coords: [1, 1, 1, 20, 18, 20, 18 , 1],
type: 'poly'
};
The HTML area circle shape, looks like what you have should work if you remove the "px" (the documentation says an array of numbers), except that it is off to the left of the icon.
working example

How do you get the rotation value of an svg object with snap.svg?

So, I'm using snap.svg and I'd like to dynamically rotate an object over time. Something like this:
function rotateObject()
{
myObject.rotation += value;
}
The problem is I don't know how to access the rotation values for my display objects (or if they even exist!) So given something simple, let's say a circle declared like this:
snap = Snap(800,600);
circle = snap.circle(400,300,50);
I know I can access the x and y values like so:
circle.attr("cx");
circle.attr("cy");
What I need help with is:
Is there a rotation property of some sort that I can use to rotate this object?
If not, how do I rotate an object with snap.svg?
Better rotate objects using Snap.Matrix()
The way suggested by Ian, works for me perfectly while I used Chrome version < 36.0
When I updated Chrome to 36.0.1985.125 I saw bug with text rotation.
So, the soulution was using
var matrix = new Snap.Matrix();
matrix.rotate(-90, x, y);
Paper.text(x, y, 'Text').attr({
fontWeight: 'bold',
fill: '#434343',
transform: matrix
});
instead of
Paper.text(x, y, 'Text').attr({
fontWeight: 'bold',
fill: '#434343',
transform: 'r-90'
});
Maybe it will be useful for somebody.
Ideally you will control the rotation yourself, (rather than figuring it out from the attributes which is possible, but fiddlier). Animation can be easier, depending on what you need. Here is an example showing some basic animation with a rect (as circle rotation is just itself if around the centre)...
s = Snap(400, 620);
var myRect = s.rect(100, 100, 100, 200).attr({
fill : 'white',
stroke : 'black'
});
var myRotate = 45;
// if you wanted to rotate manually using your own adjust variable then this
// myRect.transform("r" + myRotate);
// but simpler in most cases to use the animate method
myRect.animate( { transform: "r" + myRotate + ",150,200" }, 1000 ); //rotate around centre of 150,200
Fiddle at http://jsfiddle.net/XG7ks/6/
Really it would probably be best to get a basic grounding on transformations with SVG (and translate, rotate, scale) just for it to make a bit more sense. You can 'see' the resultant transform with myRect.attr('transform') but I would probably leave that just at first.

Resources