Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bounds Named Positions #704

Open
connordavenport opened this issue Oct 4, 2023 · 12 comments
Open

Bounds Named Positions #704

connordavenport opened this issue Oct 4, 2023 · 12 comments

Comments

@connordavenport
Copy link

Hey! Just dropping this here based on the Discord conversation! This only applies to glyphs' bounds but I liked @typesupply's suggestion for all bounds!

'''
fontParts add-on to get specific locations of a glyph's bounds
          10
     0    3     6  
       #######         
       #      #        
       #      #        
  9  1 ## 4 ##  7      
       #    #          
       #     #         
       #      #        
     2    5     8     
starting from top to bottom and left to right
0  leftTop
1  leftCenter
2  leftBottom
3  centerTop
4  centerCenter
5  centerBottom
6  rightTop
7  rightCenter
8  rightBottom
9  yCenter
10 xCenter
'''

def leftTop(self):
	xMin,yMin,xMax,yMax = self.bounds
	return (xMin,yMax)

def leftCenter(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = xMin
	y = ((yMax - yMin) / 2) + yMin
	return (x,y)

def leftBottom(self):
	xMin,yMin,xMax,yMax = self.bounds
	return (xMin,Ymin)

def centerTop(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = ((xMax - xMin) / 2) + xMin
	y = yMax
	return (x,y)

def centerCenter(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = ((xMax - xMin) / 2) + xMin
	y = ((yMax - yMin) / 2) + yMin
	return (x,y)

def centerBottom(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = ((xMax - xMin) / 2) + xMin
	y = yMin
	return (x,y)

def rightTop(self):
	xMin,yMin,xMax,yMax = self.bounds
	return (xMax,yMax)

def rightCenter(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = xMax
	y = ((yMax - yMin) / 2) + yMin
	return (x,y)

def rightBottom(self):
	xMin,yMin,xMax,yMax = self.bounds
	return (xMax,yMin)

def yCenter(self):
	xMin,yMin,xMax,yMax = self.bounds
	y = ((yMax - yMin) / 2) + yMin
	return y

def xCenter(self):
	xMin,yMin,xMax,yMax = self.bounds
	x = ((xMax - xMin) / 2) + xMin
	return x


from mojo.roboFont import RGlyph

RGlyph.leftTop = leftTop
RGlyph.leftCenter = leftCenter
RGlyph.leftBottom = leftBottom
RGlyph.centerTop = centerTop
RGlyph.centerCenter = centerCenter
RGlyph.centerBottom = centerBottom
RGlyph.rightTop = rightTop
RGlyph.rightCenter = rightCenter
RGlyph.rightBottom = rightBottom
RGlyph.yCenter = yCenter
RGlyph.xCenter = xCenter
@LettError
Copy link
Member

These are based on the bounds. Is there also a need for the same values, but based on the width, descender and ascender?

@connordavenport
Copy link
Author

A g.width.center would be nice:)

@LettError
Copy link
Member

as long as they're all read-only, right?

@connordavenport
Copy link
Author

That was my idea, yes. Not sure if Tal had other ideas but I imagine a setter for this would be a bit weird haha

@typesupply
Copy link
Member

Here's what I'm thinking of for bounds:

class Bounds(tuple):

    def _get_xMin(self):
        return self[0]

    def _get_xMax(self):
        return self[2]

    def _get_xCenter(self):
        return ((self[2] - self[0]) / 2) + self[0]

    def _get_yMin(self):
        return self[1]

    def _get_yMax(self):
        return self[3]

    def _get_yCenter(self):
        return ((self[3] - self[1]) / 2) + self[1]

    xMin = property(_get_xMin)
    xMax = property(_get_xMax)
    xCenter = property(_get_xCenter)
    yMin = property(_get_yMin)
    yMax = property(_get_yMax)
    yCenter = property(_get_yCenter)


glyph = CurrentGlyph()
bounds = Bounds(glyph.bounds)
print(bounds.xMin, bounds.xCenter)

All bounds properties would return a Bounds object. glyph.bounds, contour.bounds, component.bounds and any others.


Values based on metrics are complicated. We could add dynamic glyph.xCenter and glyph.yCenter properties that would avoid the vagaries of determining the relevant top and bottom font.info vertical metrics. Something like this:

    def _get_xCenter(self):
        return self.width / 2

    def _get_yCenter(self):
        return self.height / 2

    xCenter = property(_get_xCenter)
    yCenter = property(_get_yCenter)

@typemytype
Copy link
Member

I voter for bounds being more then just a tuple! so they can be used in different places, it just has to be a subclass of Tuple

@justvanrossum
Copy link
Contributor

Suggest to subclass typing.NamedTuple:

import typing

class Point(typing.NamedTuple):
    x: float
    y: float

class Rectangle(typing.NamedTuple):
    xMin: float
    yMin: float
    xMax: float
    yMax: float

    @property
    def center(self):
        return Point((self.xMin + self.xMax) / 2, (self.yMin + self.yMax) / 2)

b = Rectangle(10, 20, 30, 40)
print(b)
print(b.xMin)
print(b.center)

Output:

Rectangle(xMin=10, yMin=20, xMax=30, yMax=40)
10
Point(x=20.0, y=30.0)

@typesupply
Copy link
Member

I thought about a named tuple, but didn't think I could subclass it. Here's a new sketch:

import typing

class Bounds(typing.NamedTuple):

    xMin: float
    yMin: float
    xMax: float
    yMax: float

    @property
    def width(self):
        return self.xMax - self.xMin

    @property
    def xCenter(self):
        return self.xMin + (self.width / 2)

    @property
    def height(self):
        return self.yMax - self.yMin

    @property
    def yCenter(self):
        return self.yMin + (self.height / 2)


glyph = CurrentGlyph()
bounds = Bounds(*glyph.bounds)
print(isinstance(bounds, tuple))
print(bounds.xMin)
print(bounds.width)
print(bounds.xCenter)

I'm still not convinced that we need the (x, y) combination values so I've left those off.


Also, I guess I'll finally learn about decorators now. 😬

@LettError
Copy link
Member

Is this solving a problem, rather than just convenience?

@typesupply
Copy link
Member

fontParts is mostly about making things easy for scripters, and I'm tired of doing bounds calculations so I see the need for this.

@benkiel
Copy link
Member

benkiel commented Oct 6, 2023

I am all for this, yes: read only. PRs gratefully accepted!

@benkiel
Copy link
Member

benkiel commented Oct 6, 2023

PRs with tests, joyfully accepted!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants