Useful methods for graphics objects

186 views
Skip to first unread message

Kwankyu Lee

unread,
Aug 19, 2022, 5:26:31 AM8/19/22
to sage-devel
Hi,

I wish  that this would work:

sage: l = line3d([(1,2,3),(2,2,3)])
sage: l.length()
1

Is there already a handy way to get the same result in Sage?

You can image other useful methods attached to a graphics object.



Travis Scrimshaw

unread,
Aug 21, 2022, 11:04:57 PM8/21/22
to sage-devel
I am not sure how much I support that because there is no metric. If you are working with the Euclidean metric (the usual one you are used to), then you can do

sage: l = line3d([(1,2,3), (4,5,6)])
sage: V = RR^3
sage: (V(l.points[1]) - V(l.points[0])).norm()
5.19615242270663

There could also be other natural interpretations of length here, such as the number of (non-colinear) segments.

Is this more pedagogical or are you using 3d line segments in some meaningful way?

Best,
Travis

Samuel Lelievre

unread,
Aug 22, 2022, 3:45:36 AM8/22/22
to sage-devel
Travis's answer works for line3d objects consisting of a single segment.

Here is a more general version for any line3d object.

If you haven't already, install more-itertools:

```
sage: %pip install more-itertools
```

Then define this length function:

```
def line3d_euclidean_length(L):
    r"""
    Return the euclidean length of this piecewise linear path.

    INPUT:

    - ``L`` a piecewise linear path, as a ``line3d`` graphics object.
    """
    from more_itertools import pairwise
    V = RDF^3
    return sum((V(q) - V(p)).norm() for p, q in pairwise(L.points))
```

Use it as follows:
```
sage: A = line3d([(1, 0, 1), (1, 1, 1)])
sage: line3d_euclidean_length(A)
sage: 1.0

sage: B = line3d([(0, 0, 0), (1, 0, 0), (1, 1, 1)])
sage: line3d_euclidean_length(B)
2.414213562373095
```

As Travis suggests, one might also be interested in the combinatorial length:

```
def line3d_combinatorial_length(L):
    r"""
    Return the combinatorial length of this piecewise linear path.

    That is, the number of segments it consists of.

    INPUT:

    - ``L`` a piecewise linear path, as a ``line3d`` graphics object.
    """
    return Integer(len(L.points) - 1)
```

Usage:

```
sage: line3d_combinatorial_length(A)
sage: line3d_combinatorial_length(B)
```

Following Travis's suggestion, one could add an option
to combine successive segments if they are collinear.

Kwankyu Lee

unread,
Aug 23, 2022, 3:26:11 AM8/23/22
to sage-devel
On Monday, August 22, 2022 at 12:04:57 PM UTC+9 Travis Scrimshaw wrote:
I am not sure how much I support that because there is no metric.

It is of course euclidean, as you say
 
you can do

sage: l = line3d([(1,2,3), (4,5,6)])
sage: V = RR^3
sage: (V(l.points[1]) - V(l.points[0])).norm()
5.19615242270663

My point is to attach methods to graphics objects for handy computation. I am not sure if this is technically doable. 
 
There could also be other natural interpretations of length here, such as the number of (non-colinear) segments.

(1) .length() could be an alias of .line3d_euclidean_length() as this is most useful. 

(2) .line3d_combinatorial_length(): I doubt if this is useful for drawing.

Is this more pedagogical or are you using 3d line segments in some meaningful way?

Pedagogical and meaningful :) I thought of this idea while drawing graphics to prepare lecture notes for a class next semester. 
If graphics objects get more powered, then drawing (2d or 3d) mathematical pictures, which is always difficult, would get less difficult. 

For another example,  we may implement a method to compute the intersection point given two line segments (as tikz can do in latex)

Travis Scrimshaw

unread,
Aug 23, 2022, 8:18:06 PM8/23/22
to sage-devel
In general, I think we are best leaving the drawing classes to just drawing as a separations-of-concerns. It sounds like we need better integration between our algebraic objects/implementations and the drawing/plotting tools. This might include more plot_* functions or specialized mixin-/sub-classes for small dimensional (sub)spaces. Likewise we might want to add some general tools for inner product spaces, such as "ind_closest_point() or shortest_distance(), with an assumption of course that we can do calculus on the vector space.

As an alternative, if we want to think of objects specifically living in Euclidean space, we have the EuclideanSpace(n) for this with specialized subclasses for 2d and 3d. Perhaps we should implement some of the features you want using objects there, such as line (segment) as a subclass of the curve? For piecewise differentiable curves, this might require some more programming.

Éric, what do you think about adding such things to SageManifolds? Could this be feasible?

Best,
Travis

Kwankyu Lee

unread,
Aug 23, 2022, 9:14:22 PM8/23/22
to sage-devel
On Wednesday, August 24, 2022 at 9:18:06 AM UTC+9 Travis Scrimshaw wrote:
Éric, what do you think about adding such things to SageManifolds? Could this be feasible?

It is embarrassing that  an idea of attaching ".length()" method to a line segment graphics object leads to SageManifolds. We don't need vectors and inner products to compute the length of a line segment.

If we ever implement the idea, then it should be done with basic python only for "separations-of-concerns" and for modularization of sage.

 

Sébastien Labbé

unread,
Aug 24, 2022, 3:06:14 PM8/24/22
to sage-devel
Is there already a handy way to get the same result in Sage?

You can image other useful methods attached to a graphics object.

For 2d lines, it returns a Graphics object which has a length:

sage: l = line([(1,2),(10,20)])
sage: k = line([(10,20),(100,200)])
sage: len(l)
1
sage: len(k)
1

But currently, this length corresponds to the number of graphics primitives:

sage: repr(l+k)
'Graphics object consisting of 2 graphics primitives'
sage: len(l+k)
2


Kwankyu Lee

unread,
Aug 24, 2022, 3:21:49 PM8/24/22
to sage-devel
On Thursday, August 25, 2022 at 4:06:14 AM UTC+9 Sébastien Labbé wrote:
Is there already a handy way to get the same result in Sage?

You can image other useful methods attached to a graphics object.

For 2d lines, it returns a Graphics object which has a length.

Right. That every graphics object (line, circle, etc) is wrapped in Graphics object seems to make the implementation not straightforward...
 

Travis Scrimshaw

unread,
Aug 24, 2022, 10:33:55 PM8/24/22
to sage-devel
Yes we do, otherwise it doesn't make sense mathematically. There are lots of different metrics I can use on 2D vectors that make sense. Otherwise any (physical paper) maps would be very impractical. I think it is generally bad practice to assign mathematical significant things to classes that are not used for mathematical purposes.

I have no idea what your argument is for the second part. Separations of concerns means object A (in this case, an object for drawing something in 3D) only does things that are meant for object A (drawing stuff) and not do things that are meant for object B (a vector or line segment in Euclidean space). It has nothing to do with what you use to actually do the implementation. If you are worried about linking together stuff that shouldn't be needed, that to me is a code smell.

Best,
Travis

Kwankyu Lee

unread,
Aug 25, 2022, 1:29:58 AM8/25/22
to sage-devel
I have no idea what your argument is for the second part. Separations of concerns means ...

I didn't use the phrase in its precise meaning (so I quoted it) 

I meant that you can write pythagorean formula, which is just what you need to compute the length of a line segment object,  without using Sage vectors and norms heavy machinery. For drawing, you only need simple maths. Moreover "Python only" seems necessary because of sage modularization effort.  

 

Eric Gourgoulhon

unread,
Aug 25, 2022, 4:38:52 AM8/25/22
to sage-devel
Hi,

Le mercredi 24 août 2022 à 02:18:06 UTC+2, Travis Scrimshaw a écrit :
In general, I think we are best leaving the drawing classes to just drawing as a separations-of-concerns.

+1
IMHO, line2d and line3d should not be considered as mathematical objects (segmented lines in some Euclidean space), but rather as pure graphical objects.
 
It sounds like we need better integration between our algebraic objects/implementations and the drawing/plotting tools. This might include more plot_* functions or specialized mixin-/sub-classes for small dimensional (sub)spaces. Likewise we might want to add some general tools for inner product spaces, such as "ind_closest_point() or shortest_distance(), with an assumption of course that we can do calculus on the vector space.

+1
 

As an alternative, if we want to think of objects specifically living in Euclidean space, we have the EuclideanSpace(n) for this with specialized subclasses for 2d and 3d. Perhaps we should implement some of the features you want using objects there, such as line (segment) as a subclass of the curve? For piecewise differentiable curves, this might require some more programming.

Éric, what do you think about adding such things to SageManifolds? Could this be feasible?


This certainly should be feasible. As you point out, one should introduce a subclass of curves for segmented lines in Eudlidean spaces and define a length() method for them. More generally, one should introduce a length() method for any piecewise differentiable curve in a pseudo-Riemannian manifold. This is not implemented yet and certainly should be added to the todo list.

Best regards, 

  Eric.

Eric Gourgoulhon

unread,
Aug 25, 2022, 4:55:21 AM8/25/22
to sage-devel
Le mercredi 24 août 2022 à 03:14:22 UTC+2, Kwankyu Lee a écrit :
It is embarrassing that  an idea of attaching ".length()" method to a line segment graphics object leads to SageManifolds. We don't need vectors and inner products to compute the length of a line segment.

I agree that it sounds daunting to speak about pseudo-Riemannian manifolds for just computing the length of a line segment, but note that, by working at the EuclideanSpace level, the end user does not need to know anything about the underlying manifold machinery.  See for instance this tutorial: https://doc.sagemath.org/html/en/thematic_tutorials/vector_calculus.html

The only complication would be to add the extra line
sage: E = EuclideanSpace(3)
at the beginning of the code For instance, your example at the start of this thread would become

sage: E = EuclideanSpace(3)
sage: l = E.line([(1,2,3),(2,2,3)])  # not implemented yet
sage: l.length()
1

Eric.
 

Kwankyu Lee

unread,
Aug 25, 2022, 10:23:41 AM8/25/22
to sage-devel
Let me expand the example a bit.

We are given two points. We draw a line segment connecting the two points. Then we want to draw a circle with a diameter the line segment.

Certainly we need to compute the center and the radius of the circle. The standard way to do this with Sage is, as Travis described, to do some vector calculus.

But an intuitive way (or an object-oriented way ?) is to get the necessary information from the line segment. Thus if L is the line segment, I do p = L.mid_point(); r = L.length() / 2. Then circle(p, r) will do the work.

On Thursday, August 25, 2022 at 5:55:21 PM UTC+9 Eric Gourgoulhon wrote:
sage: E = EuclideanSpace(3)
sage: L = E.line([(1,2,3),(2,2,3)])  # not implemented yet
sage: L.length()

For the above example,  we create the line L and the circle C in EuclideanSpace(2). Then we do L.draw() + C.draw(). This is an interesting idea. But this would make graphics module a dependency of Euclidean space module.

By the way, the purpose of my original post is to ask whether providing the facility for the intuitive way of drawing (algorithmic drawing or object-oriented drawing, to invent a fancy term) is worth while or just waste of energy...

Nils Bruin

unread,
Aug 25, 2022, 1:57:15 PM8/25/22
to sage-devel
On Thursday, 25 August 2022 at 07:23:41 UTC-7 Kwankyu Lee wrote:
But an intuitive way (or an object-oriented way ?) is to get the necessary information from the line segment. Thus if L is the line segment, I do p = L.mid_point(); r = L.length() / 2. Then circle(p, r) will do the work.

That sounds like the model that geogebra uses. Geogebra is immensely useful for teaching and simple illustrations especially because it's point-and-click and it's designed with basically the level of first year calculus in mind. Plus it can export to very efficient tikz, which makes it easy to make point-and-click pictures and still have things mathematically accurate (for instance, points can "snap" to nearby graphical objects, including graphs of functions.

It could be interesting to have "constructive geometry" objects in python (sympy?) or sage, but I suspect that should start as separate objects from graphics objects to start with (although you could try subclassing). I do think that having a graphical interface such as geogebra has is really a plus for these applications, though, so I have some doubts on whether you'd be able to successfully compete with it.

Personally, I think the graphics objects in sage should primarily be such that it is very easy to get at the underlying mathplotlib objects. In my experience, the tweaking of graphics for publication is what takes by far the most time, so the more interface for that the better. Matplotlib has a lot of use documentation, and examples, so being able to apply the knowledge obtained from there helps.
 

Kwankyu Lee

unread,
Aug 25, 2022, 7:04:02 PM8/25/22
to sage-devel
On Friday, August 26, 2022 at 2:57:15 AM UTC+9 Nils Bruin wrote:
On Thursday, 25 August 2022 at 07:23:41 UTC-7 Kwankyu Lee wrote:
But an intuitive way (or an object-oriented way ?) is to get the necessary information from the line segment. Thus if L is the line segment, I do p = L.mid_point(); r = L.length() / 2. Then circle(p, r) will do the work.

That sounds like the model that geogebra uses.

Yes. It would be a programming interface of the model.

I do think that having a graphical interface such as geogebra has is really a plus for these applications, though, 

No intention to compete with geogebra with a graphical interface. 

Rather it would be a competition as like latex vs word. Really tikz does that in the latex world. But sage could have some functionality of tikz, working directly with sage graphics objects.
 
In my experience, the tweaking of graphics for publication is what takes by far the most time, so the more interface for that the better. 

Yes. We can view it as a way to tweak the graphics objects to get the matplotlib object we want. For example, we can easily draw a pentagon in sage, but tweaking the pentagon using the edges and vertices of the pentagon seems difficult. 

 

Kwankyu Lee

unread,
Aug 27, 2022, 1:20:33 AM8/27/22
to sage-devel
On Friday, August 26, 2022 at 8:04:02 AM UTC+9 Kwankyu Lee wrote:
On Friday, August 26, 2022 at 2:57:15 AM UTC+9 Nils Bruin wrote:
On Thursday, 25 August 2022 at 07:23:41 UTC-7 Kwankyu Lee wrote:
But an intuitive way (or an object-oriented way ?) is to get the necessary information from the line segment. Thus if L is the line segment, I do p = L.mid_point(); r = L.length() / 2. Then circle(p, r) will do the work.

That sounds like the model that geogebra uses.

Yes. It would be a programming interface of the model.

Now I have this idea. (Sorry for the vague original post.) We add new class "GeometricFigure2d" and "GeometricFigure3d". Line segments, circle, etc would be subclasses. So we can do

l = figures.Line((1,2,3), (2,4,6))
p = l.mid_point()
r = l.length()
c = figures.Circle(p, r)
c.render(color="green")  # to get the rendered graphics object

We may say that the classes "GeometricFigure2(3)d"  implements descriptive geometry but with coordinates, for drawing purpose but not geometry.



 

Nils Bruin

unread,
Aug 27, 2022, 1:57:49 AM8/27/22
to sage-devel
Do keep in mind that there is a basic command-driven interface behind Geogebra as well. You can actually literally enter the following into their "3D Calculator":

A=(1,2,3)
B=(4,5,6)
L=Segment(A, B)
P=Midpoint(L)
r=Length(L)
C=Circle(P, r)

where the system automatically translates the last command (which does not fully determine a circle in 3-space) to:

C: Circle(P,r,xOyPlane)

It looks like it's difficult to get at their programmatic interface. They do have scripting, which may provide an interface that's a little easier to work with.

It may still be that there's a market for this in sagemath/python; or it may be an enjoyable recreational project to work on. But before you set off on it, it's probably good to know what prior art is available, and it looks like at least the start of what you'd be doing is reimplementing a lot of the things geogebra already has.

On the python front, it looks like Sympy (which we already include) may have a start for you: https://docs.sympy.org/latest/modules/geometry/

Kwankyu Lee

unread,
Aug 27, 2022, 9:42:50 AM8/27/22
to sage-devel
On Saturday, August 27, 2022 at 2:57:49 PM UTC+9 Nils Bruin wrote:
Do keep in mind that there is a basic command-driven interface behind Geogebra as well. 

Yes. Thank you.
 
which does not fully determine a circle in 3-space

I was careless. I should have listed two coordinates for 2d line.
 
It may still be that there's a market for this in sagemath/python; or it may be an enjoyable recreational project to work on. But before you set off on it, it's probably good to know what prior art is available, and it looks like at least the start of what you'd be doing is reimplementing a lot of the things geogebra already has.

If there's no market of sage users, then there is no reason to start. If there is, we can do the work bit by bit.
 
On the python front, it looks like Sympy (which we already include) may have a start for you: https://docs.sympy.org/latest/modules/geometry/

Thank you very much for helpful comments and info!
 
Reply all
Reply to author
Forward
0 new messages