NAME
Math::Geometry::Construction
- intersecting lines and circles
VERSION
Version 0.019
SYNOPSIS
use Math::Geometry::Construction;
my $construction = Math::Geometry::Construction->new
(background => 'white');
my $p1 = $construction->add_point('x' => 100, 'y' => 150, hidden => 1);
my $p2 = $construction->add_point('x' => 120, 'y' => 150, hidden => 1);
my $p3 = $construction->add_point('x' => 200, 'y' => 50);
my $p4 = $construction->add_point('x' => 200, 'y' => 250);
my $l1 = $construction->add_line(extend => 10,
label => 'g',
label_offset_y => 13,
support => [$p1, $p2]);
my $l2 = $construction->add_line(support => [$p3, $p4]);
my $i1 = $construction->add_derivate('IntersectionLineLine',
input => [$l1, $l2]);
my $p5 = $i1->create_derived_point(label => 'S',
label_offset_x => 5,
label_offset_y => -5);
my $c1 = $construction->add_circle(center => $p5, radius => 20);
# an intersection point can also be generated in one go
my $p6 = $construction->add_derived_point
('IntersectionCircleLine',
{input => [$l1, $c1]},
{position_selector => ['extreme_position', [[1, 0]]]});
print $construction->as_svg(width => 800, height => 300)->xmlify;
DESCRIPTION
This is alpha software. The API is stabilizing and the test suite at least deserves that name by now, but input checks and documentation are still sparse. However, release early, release often, so here we go.
Please note: In version 0.014, the underlying vector class has been changed from Math::VectorReal to Math::Vector::Real. The advantage of the latter is that it natively supports 2D vectors. Math::Geometry::Construction
continues to accept Math::VectorReal objects in constructors, but the relevant accessors and mutators will return or accept only Math::Vector::Real objects. Many basic applications will not make use of these features and therefore will continue to work without change. Applications making use of them, however, will need to be adapted.
Aims
This distribution serves two purposes:
Carrying out geometric constructions like with compass and straight edge. You can define points, lines through two points, and circles around a given center and through a given point. You can let these objects intersect to gain new points to work with.
Creating illustrations for geometry. This task is similar to the one above, but not the same. For illustrations, the priorities are usually different and more powerful tools like choosing a point on a given object, drawing circles with fixed radius etc. are handy.
Motivation
Problems of these kinds can be solved using several pieces of geometry software. However, I have not found any with sufficiently powerful automization features. This problem has two aspects:
For some project, I want to create many illustrations. I have certain rules for the size of output images, usage of colors etc.. With the programs I have used so far, I have found it difficult to set these things in a consistent way for all of my illustrations without the need to set them each time I start the program or to change consistently later on.
For actual constructions, most macro languages are not powerful enough. For example, the intersection between two circles sometimes yields two points. There are situations where I want to choose the one which is further away from some given point or the one which is not the one I already had before or things like that. The macro languages of most geometry programs do not allow that. It is somehow determined internally which one is first intersection point and which the second, so from the user point of view the choice is arbitrary. Or, for example, I have come across the situation where I needed to double an angle iteratively until it becomes larger than a given angle. Impossible in most macro languages. With this module, you have Perl as your "macro language".
Output Formats
The current output formats are SVG
and TikZ
. Especially the latter one is experimental and the interface might change in future versions. Other output engines could be written by subclassing Math::Geometry::Construction::Draw. However, documentation of the interface is not available, yet.
Intersection Concept
Intersecting two objects consists of two steps. First, you create a Math::Geometry::Construction::Derivate object that holds the intersecting partners and "knows" how to calculate the intersection points. Second, you create a Math::Geometry::Construction::DerivedPoint from the Derivate
. The DerivedPoint
object holds information about which of the intersection points to use. This can be based on distance to a given point, being the extreme point with respect to a given direction etc..
The DerivedPoint
object only holds information about how to select the right point. Only when you ask for the position of the point it is actually calculated. The purpose of this approach is that you will always get the desired point based on the current situation, even if you move your start configuration and the arrangement of points changes.
The classes are called Derivate
and DerivedPoint
because this concept is applicable beyond the case of intersections. It could, for example, be used to calculate the center of a circle given by three points. Whenever some operation based on given objects results in a finite number of points, it fits into this concept.
Current Status
At the moment, you can define points, lines, and circles. You can intersect circles and lines with each other. The objects can have labels, but the automatic positioning of the labels is very primitive and unsatisfactory withouth polishing by the user.
Next Steps
Extend documentation
Improve performance
Improve automatic positioning of labels
Improve test suite along the way
INTERFACE
Constructors
new
$construction = Math::Geometry::Construction->new(%args)
Creates a new Math::Geometry::Construction
object and initializes attributes. This is the default Moose constructor.
Public Attributes
background
By default the background is transparent. This attribute can hold a color to be used instead. Possible values depend on the output type. For SVG
, it can hold any valid SVG
color specifier, e.g. white
or rgb(255, 255, 255)
. TikZ
currently ignores the background
attribute.
objects
A construction holds a hash of the objects it contains. The hash itself is inaccessible. However, you can call the following accessors:
count_objects
Returns the number of objects. For the Moose aficionado: This is the
count
method of theHash
trait.object
$construction->object($key) $construction->object($key, $value)
Accessor/mutator method for single entries of the hash. The keys are the object IDs. Usage of the mutator is not intended, use only to tamper with the internals at your own risk.
This is the
accessor
method of theHash
trait.object_ids
Returns a (copy of) the list of keys. This is the
keys
method of theHash
trait.objects
Returns a (copy of) the list of values. This is the
values
method of theHash
trait.
As more specific accessors there are
points
lines
circles
The points
list contains both user defined points and derived points.
point_size
Holds the default point size that is used if no explict size is given to Point
objects. Defaults to 6
. Changing it will only affect Point
objects created afterwards.
Methods
add_point
$construction->add_point(%args)
Returns a new Math::Geometry::Construction::Point. All parameters are handed over to the constructor after adding the construction
and order_index
arguments.
Examples:
$construction->add_point(position => [10, 20]);
$construction->add_point('x' => 50, 'y' => 30,
style => {stroke => 'red'});
# requires 'use Math::Vector::Real' in this package
$construction->add_point(position => V(-15, 23),
hidden => 1);
# NB: use of Math::VectorReal is still supported, but discouraged
# in favor of Math::Vector::Real
# requires 'use Math::VectorReal' in this package
$construction->add_point(position => vector(-1.3, 2.7, 0),
size => 10);
add_line
$construction->add_line(%args)
Returns a new Math::Geometry::Construction::Line. All parameters are handed over to the constructor after adding the construction
and order_index
arguments.
Example:
$construction->add_line(support => [$point1, $point2],
extend => 10);
add_circle
$construction->add_circle(%args)
Returns a new Math::Geometry::Construction::Circle. All parameters are handed over to the constructor after adding the construction
and order_index
arguments.
The "standard" circle requires the center and a point "support" point on its perimeter. However, you can provide a radius instead of the support point, and the constructor of Math::Geometry::Construction::Circle will create a support point under the hood. Even if you move the center later on, the radius of the circle will stay constant.
Examples:
$construction->add_circle(center => $point1,
support => $point2);
$construction->add_circle(center => $point1,
radius => 50);
add_object
$construction->add_object($class, %args)
Returns a new instance of the given class. All parameters are handed over to the constructor after adding the construction
and order_index
arguments. In fact, add_point, add_line, and add_circle just call this method with the appropriate class.
add_derived_point
$construction->add_derived_point($class, $derivate_args, $point_args)
Combines the creation of a Derivate
object and a DerivedPoint
object in one step.
The method expects three parameters:
- 1. the derivate class
- 2. a hash reference with arguments for the constructor of Math::Geometry::Construction::Derivate
- 3. a hash reference with arguments for the constructor of Math::Geometry::Construction::DerivedPoint; this argument is optional, if not defined it is replaced by an empty hash reference
Returns the DerivedPoint
object.
Example:
$derived_point = $construction->add_derived_point
('IntersectionLineLine',
{input => [$line1, $line2]},
{position_selector => ['indexed_point', [0]]});
In this example, the last hash reference can be omitted:
$derived_point = $construction->add_derived_point
('IntersectionLineLine', {input => [$line1, $line2]});
The missing hash reference is replaced by an empty hash reference, and the constructor of the DerivedPoint
object uses the default position selector ['indexed_point', [0]]
.
If multiple derived points based on the same derivative are desired then the third argument for add_derived_point
can be replaced by the reference to an array of hash references each of which holds the parameters for one of the points. A list of DerivedPoint
objects is returned.
Example:
@derived_points = $construction->add_derived_point
('IntersectionCircleLine',
{input => [$circle, $line]},
[{position_selector => ['extreme_point', [[0, -1]]]},
{position_selector => ['extreme_point', [[0, 1]]]}]);
In this case, we ask for the two intersection points between a circle and a line. The extreme_point
position selector will give us the most extreme of the intersection points in the given direction. Therefore, in SVG
coordinates, $derived_points[0]
will hold the "northern", $derived_points[1]
the "southern" intersection point.
add_derivate
$construction->add_derivate($class, %args)
Creates and returns a Math::Geometry::Construction::Derivate subclass instance. This can be used to create DerivedPoint
objects. In most cases, it is convenient to perform these two steps in one go, see add_derived_point.
This method is a convenience shortcut for add_object. The only difference is that $class
is prepended with Math::Geometry::Construction::Derivate::
. Therefore you can call
$construction->add_derivate('IntersectionCircleLine', %args)
instead of
$construction->add_object
('Math::Geometry::Construction::Derivate::IntersectionCircleLine', %args)
Example:
$derivate = $construction->add_derivate('TranslatedPoint',
input => [$point],
translator => [10, -20]);
$point = $derivate->create_derived_point;
draw
$construction->draw('SVG', %args)
Draws the construction. The return value depends on the output type and might be an object or a stringified version. Currently, the only output types are SVG
and TikZ
. See as_svg and as_tikz.
If the type does not contain a ::
then it is prepended by Math::Geometry::Construction::Draw::
before requiring the module.
Calls the draw
method first on all non-point objects, then on all Point
and DerivedPoint
objects. This is because I think that points should be drawn on top of lines, circles etc..
as_svg
$construction->as_svg(%args)
$construction->draw('SVG', %args)
Shortcut for draw. Returns an SVG object representing the construction. All parameters are handed over to the SVG constructor. At least width
and height
should be provided.
If a background color is specified then a rectangle of that color is drawn as background. The size is taken from the viewBox
attribute if specified, from width
and height
otherwise. If none is given, no background is drawn.
as_tikz
$construction->as_tikz(%args)
$construction->draw('TikZ', %args)
Shortcut for draw. Returns an LaTeX::TikZ sequence object representing the construction. See Math::Geometry::Construction::Draw and Math::Geometry::Construction::Draw::TikZ for supported parameters. At least width
and height
should be provided.
List of Derivates
Partial Drawing
Each line or similar object holds a number of "points of interest". These are - in case of the line - the two points that define the line and all intersection points the line is involved in. At drawing time, the object determines the most extreme points and they define the end points of the drawn line segment. The extend
attribute allows to extend the line for a given length beyond these points because this often looks better. A similar concept will be implemented for circles, but currently, the full circle is drawn.
Reusing Objects
Labels
DIAGNOSTICS
Exceptions
Currently, Math::Geometry::Construction
does not use any advanced exception framework, it just croaks if it is unhappy. The error messages are listed below in alphabetical order.
A line needs exactly two support points
Thrown by the constructor of Math::Geometry::Construction::Line if the array referenced by the
support
parameter does not contain exactly two elements. The type of the elements is (not yet) checked by Moose.Class name %s did not pass regex check
Need circles for CircleCircle intersection, no %s
Need one circle and one line to intersect
The
input
for circle line intersection has to be exactly one Math::Geometry::Construction::Circle (or subclass) object and one Math::Geometry::Construction::Line (or subclass) object. This exception is thrown in all other cases. It might be split into more specific exceptions in the future. The exception is thrown only when the positions of the intersections are calculated.Need lines for LineLine intersection, no %s
The
input
for line line intersection has to consist of exactly two Math::Geometry::Construction::Line (or subclass) objects. If the correct number of items is given, but one of them is of an incorrect type then this exception is thrown.Need one line for PointOnLine"
Need one point
Need something with a position, no %s
Need two circles to intersect
Need two lines to intersect
The
input
for line line intersection has to consist of exactly two Math::Geometry::Construction::Line (or subclass) objects. If the wrong number ofinput
items (ignoring their values) is given then this exception is thrown. The exception is thrown only when the position of the intersection is calculated.No way to determine position of PointOnLine %s
Position of PointOnLine has to be set somehow
When constructing a Math::Geometry::Construction::Derivate::PointOnLine object, one of the attributes
distance
,quantile
,x
, andy
has to be specified. Otherwise this exception is thrown.Unable to load module %s: %s
Undefined direction in 'extreme_position' selector
The
extreme_position
position selector expects a direction vector. This exception is raised if the provided direction is undefined.Undefined index in 'indexed_position' selector"
The
indexed_position
position selector expects an index. This exception is raised if the provided index is undefined.Undefined reference position in '%s' selector
The
close_position
anddistant_position
position selectors expect a reference position. This exception is raised if the provided reference is undefined.Unsupported vector format %s
Warnings
Failed to parse viewBox attribute.
Method position must be overloaded
Method id must be overloaded
No positions to select from in %s.
This warning is issued by the position selectors if there is are no positions. For example, if you are using an intersection point of two circles, but the circles do not intersect. The position selector will print this warning and return undef. Your downstream code must be able to handle undefined positions.
Position index out of range in %s.
The 'radius' attribute of Math::Geometry::Construction::Point is deprecated and might be removed in a future version. Use 'size' with the double value (diameter of the circle) instead.
I think this message speaks for itself :-).
Support points of line %s are identical, cannot determine normal.
Undefined center of circle %s, nothing to draw.
Undefined position of point %s, nothing to draw.
Undefined support of circle %s, nothing to draw.
Undefined support point in line %s, cannot determine normal.
Undefined support point in line %s, nothing to draw.
BUGS AND LIMITATIONS
Please report any bugs or feature requests to bug-math-geometry-construction at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Math-Geometry-Construction. I will be notified, and then you will automatically be notified of progress on your bug as I make changes.
AUTHOR
Lutz Gehlen, <perl at lutzgehlen.de>
LICENSE AND COPYRIGHT
Copyright 2011 Lutz Gehlen.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.