1 // Copyright (C) 2003 Maurizio Paolini <paolini@dmf.unicatt.it>
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 #include "polygon_type.h"
20 #include "bogus_imp.h"
22 #include "point_imp.h"
23 #include "polygon_imp.h"
24 #include "object_calcer.h"
26 #include "../misc/common.h"
33 * triangle by its vertices
36 static const char triangle_constructstatement
[] = I18N_NOOP( "Construct a triangle with this vertex" );
37 static const char triangle_constructstatement2
[] = I18N_NOOP( "Select a point to be a vertex of the new triangle..." );
39 static const struct ArgsParser::spec argsspecTriangleB3P
[] =
41 { PointImp::stype(), triangle_constructstatement
, triangle_constructstatement2
, true },
42 { PointImp::stype(), triangle_constructstatement
, triangle_constructstatement2
, true },
43 { PointImp::stype(), triangle_constructstatement
, triangle_constructstatement2
, true }
46 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( TriangleB3PType
)
48 TriangleB3PType::TriangleB3PType()
49 : ArgsParserObjectType( "TriangleB3P", argsspecTriangleB3P
, 3 )
53 TriangleB3PType::~TriangleB3PType()
57 const TriangleB3PType
* TriangleB3PType::instance()
59 static const TriangleB3PType s
;
63 ObjectImp
* TriangleB3PType::calc( const Args
& parents
, const KigDocument
& ) const
65 if ( ! margsparser
.checkArgs( parents
, 1 ) ) return new InvalidImp
;
66 std::vector
<Coordinate
> points
;
68 Coordinate centerofmass3
= Coordinate( 0, 0 );
69 for ( Args::const_iterator i
= parents
.begin(); i
!= parents
.end(); ++i
)
71 Coordinate point
= static_cast<const PointImp
*>( *i
)->coordinate();
72 centerofmass3
+= point
;
73 points
.push_back( point
);
75 return new PolygonImp( 3, points
, centerofmass3
/3 );
78 const ObjectImpType
* TriangleB3PType::resultId() const
80 return PolygonImp::stype();
83 bool TriangleB3PType::canMove( const ObjectTypeCalcer
& o
) const
85 return isFreelyTranslatable( o
);
88 bool TriangleB3PType::isFreelyTranslatable( const ObjectTypeCalcer
& o
) const
90 std::vector
<ObjectCalcer
*> parents
= o
.parents();
91 return parents
[0]->isFreelyTranslatable() &&
92 parents
[1]->isFreelyTranslatable() &&
93 parents
[2]->isFreelyTranslatable();
96 void TriangleB3PType::move( ObjectTypeCalcer
& o
, const Coordinate
& to
,
97 const KigDocument
& d
) const
99 std::vector
<ObjectCalcer
*> parents
= o
.parents();
100 assert( margsparser
.checkArgs( parents
) );
101 const Coordinate a
= static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
102 const Coordinate b
= static_cast<const PointImp
*>( parents
[1]->imp() )->coordinate();
103 const Coordinate c
= static_cast<const PointImp
*>( parents
[2]->imp() )->coordinate();
104 if ( parents
[0]->canMove() )
105 parents
[0]->move( to
, d
);
106 if ( parents
[1]->canMove() )
107 parents
[1]->move( to
+ b
- a
, d
);
108 if ( parents
[2]->canMove() )
109 parents
[2]->move( to
+ c
- a
, d
);
112 const Coordinate
TriangleB3PType::moveReferencePoint( const ObjectTypeCalcer
& o
) const
114 std::vector
<ObjectCalcer
*> parents
= o
.parents();
115 assert( margsparser
.checkArgs( parents
) );
116 return static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
119 std::vector
<ObjectCalcer
*> TriangleB3PType::movableParents( const ObjectTypeCalcer
& ourobj
) const
121 std::vector
<ObjectCalcer
*> parents
= ourobj
.parents();
122 std::set
<ObjectCalcer
*> ret
;
123 std::vector
<ObjectCalcer
*> tmp
= parents
[0]->movableParents();
124 ret
.insert( tmp
.begin(), tmp
.end() );
125 tmp
= parents
[1]->movableParents();
126 ret
.insert( tmp
.begin(), tmp
.end() );
127 tmp
= parents
[2]->movableParents();
128 ret
.insert( tmp
.begin(), tmp
.end() );
129 ret
.insert( parents
.begin(), parents
.end() );
130 return std::vector
<ObjectCalcer
*>( ret
.begin(), ret
.end() );
137 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBNPType
)
139 PolygonBNPType::PolygonBNPType()
140 : ObjectType( "PolygonBNP" )
144 PolygonBNPType::~PolygonBNPType()
148 const PolygonBNPType
* PolygonBNPType::instance()
150 static const PolygonBNPType s
;
154 ObjectImp
* PolygonBNPType::calc( const Args
& parents
, const KigDocument
& ) const
156 uint count
= parents
.size();
157 assert (count
>= 3); /* non sono ammessi poligoni con meno di tre lati */
158 // if ( parents[0] != parents[count] ) return new InvalidImp;
159 std::vector
<Coordinate
> points
;
162 Coordinate centerofmassn
= Coordinate( 0, 0 );
164 for ( uint i
= 0; i
< count
; ++i
)
167 Coordinate point
= static_cast<const PointImp
*>( parents
[i
] )->coordinate();
168 centerofmassn
+= point
;
169 points
.push_back( point
);
171 return new PolygonImp( npoints
, points
, centerofmassn
/npoints
);
174 const ObjectImpType
* PolygonBNPType::resultId() const
176 return PolygonImp::stype();
179 const ObjectImpType
* PolygonBNPType::impRequirement( const ObjectImp
*, const Args
& ) const
181 return PointImp::stype();
184 bool PolygonBNPType::isDefinedOnOrThrough( const ObjectImp
*, const Args
& ) const
186 return false; /* should be true? */
189 std::vector
<ObjectCalcer
*> PolygonBNPType::sortArgs( const std::vector
<ObjectCalcer
*>& args
) const
191 return args
; /* should already be in correct order */
194 Args
PolygonBNPType::sortArgs( const Args
& args
) const
199 bool PolygonBNPType::canMove( const ObjectTypeCalcer
& o
) const
201 return isFreelyTranslatable( o
);
204 bool PolygonBNPType::isFreelyTranslatable( const ObjectTypeCalcer
& o
) const
206 std::vector
<ObjectCalcer
*> parents
= o
.parents();
207 for ( uint i
= 0; i
< parents
.size(); ++i
)
209 if ( !parents
[i
]->isFreelyTranslatable() ) return false;
214 void PolygonBNPType::move( ObjectTypeCalcer
& o
, const Coordinate
& to
,
215 const KigDocument
& d
) const
217 std::vector
<ObjectCalcer
*> parents
= o
.parents();
218 const Coordinate ref
= static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
219 for ( uint i
= 0; i
< parents
.size(); ++i
)
221 const Coordinate a
= static_cast<const PointImp
*>( parents
[i
]->imp() )->coordinate();
222 parents
[i
]->move( to
+ a
- ref
, d
);
226 const Coordinate
PolygonBNPType::moveReferencePoint( const ObjectTypeCalcer
& o
229 std::vector
<ObjectCalcer
*> parents
= o
.parents();
230 return static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
233 std::vector
<ObjectCalcer
*> PolygonBNPType::movableParents( const ObjectTypeCalcer
& ourobj
) const
235 std::vector
<ObjectCalcer
*> parents
= ourobj
.parents();
236 std::set
<ObjectCalcer
*> ret
;
237 for ( uint i
= 0; i
< parents
.size(); ++i
)
239 std::vector
<ObjectCalcer
*> tmp
= parents
[i
]->movableParents();
240 ret
.insert( tmp
.begin(), tmp
.end() );
242 ret
.insert( parents
.begin(), parents
.end() );
243 return std::vector
<ObjectCalcer
*>( ret
.begin(), ret
.end() );
247 * regular polygon by center and vertex
250 //static const char constructpoligonthroughpointstat[] = I18N_NOOP( "Construct a polygon with this vertex" );
252 //static const char constructpoligonwithcenterstat[] = I18N_NOOP( "Construct a polygon with this center" );
254 //static const ArgsParser::spec argsspecPoligonBCV[] =
256 // { PointImp::stype(), constructpoligonwithcenterstat,
257 // I18N_NOOP( "Select the center of the new polygon..." ), false },
258 // { PointImp::stype(), constructpoligonthroughpointstat,
259 // I18N_NOOP( "Select a vertex for the new polygon..." ), true },
260 // { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false }
263 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonBCVType
)
265 PolygonBCVType::PolygonBCVType()
266 : ObjectType( "PoligonBCV" )
267 // we keep the name "PoligonBCV" although syntactically incorrect for
268 // compatibility reasons with old kig files
269 // : ArgsParserObjectType( "PoligonBCV", argsspecPoligonBCV, 3 )
273 PolygonBCVType::~PolygonBCVType()
277 const PolygonBCVType
* PolygonBCVType::instance()
279 static const PolygonBCVType s
;
283 ObjectImp
* PolygonBCVType::calc( const Args
& parents
, const KigDocument
& ) const
285 if ( parents
.size() < 3 || parents
.size() > 4 ) return new InvalidImp
;
286 for ( uint i
= 0; i
< 2; ++i
)
288 if ( ! parents
[0]->inherits( PointImp::stype() ) ) return new InvalidImp
;
291 if ( ! parents
[2]->inherits( IntImp::stype() ) ) return new InvalidImp
;
293 const Coordinate center
=
294 static_cast<const PointImp
*>( parents
[0] )->coordinate();
295 const Coordinate vertex
=
296 static_cast<const PointImp
*>( parents
[1] )->coordinate();
298 static_cast<const IntImp
*>( parents
[2] )->data();
300 if ( parents
.size() == 4 )
302 if ( ! parents
[3]->inherits( IntImp::stype() ) ) return new InvalidImp
;
303 twist
= static_cast<const IntImp
*>( parents
[3] )->data();
305 std::vector
<Coordinate
> vertexes
;
307 double dx
= vertex
.x
- center
.x
;
308 double dy
= vertex
.y
- center
.y
;
310 for ( int i
= 1; i
<= sides
; i
++ )
312 double alfa
= 2*twist
*M_PI
/sides
;
313 double theta1
= alfa
*i
- alfa
;
314 double ctheta1
= cos(theta1
);
315 double stheta1
= sin(theta1
);
317 Coordinate v1
= center
+ Coordinate( ctheta1
*dx
- stheta1
*dy
,
318 stheta1
*dx
+ ctheta1
*dy
);
319 vertexes
.push_back( v1
);
321 return new PolygonImp( uint (sides
), vertexes
, center
);
324 const ObjectImpType
* PolygonBCVType::resultId() const
326 return SegmentImp::stype();
329 const ObjectImpType
* PolygonBCVType::impRequirement( const ObjectImp
* obj
, const Args
& ) const
331 if ( obj
->inherits( PointImp::stype() ) )
332 return PointImp::stype();
334 if ( obj
->inherits( IntImp::stype() ) )
335 return IntImp::stype();
340 bool PolygonBCVType::isDefinedOnOrThrough( const ObjectImp
*, const Args
& ) const
342 return false; /* should be true? */
345 std::vector
<ObjectCalcer
*> PolygonBCVType::sortArgs( const std::vector
<ObjectCalcer
*>& args
) const
347 return args
; /* should already be in correct order */
350 Args
PolygonBCVType::sortArgs( const Args
& args
) const
355 bool PolygonBCVType::canMove( const ObjectTypeCalcer
& o
) const
357 return isFreelyTranslatable( o
);
360 bool PolygonBCVType::isFreelyTranslatable( const ObjectTypeCalcer
& o
) const
362 std::vector
<ObjectCalcer
*> parents
= o
.parents();
363 return parents
[0]->isFreelyTranslatable() &&
364 parents
[1]->isFreelyTranslatable();
367 void PolygonBCVType::move( ObjectTypeCalcer
& o
, const Coordinate
& to
,
368 const KigDocument
& d
) const
370 std::vector
<ObjectCalcer
*> parents
= o
.parents();
371 // assert( margsparser.checkArgs( parents ) );
372 if ( ! parents
[0]->imp()->inherits( PointImp::stype() ) ||
373 ! parents
[1]->imp()->inherits( PointImp::stype() ) ) return;
375 const Coordinate a
= static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
376 const Coordinate b
= static_cast<const PointImp
*>( parents
[1]->imp() )->coordinate();
377 parents
[0]->move( to
, d
);
378 parents
[1]->move( to
+ b
- a
, d
);
381 const Coordinate
PolygonBCVType::moveReferencePoint( const ObjectTypeCalcer
& o
) const
383 std::vector
<ObjectCalcer
*> parents
= o
.parents();
384 // assert( margsparser.checkArgs( parents ) );
385 if ( ! parents
[0]->imp()->inherits( PointImp::stype() ) ) return Coordinate::invalidCoord();
387 return static_cast<const PointImp
*>( parents
[0]->imp() )->coordinate();
390 std::vector
<ObjectCalcer
*> PolygonBCVType::movableParents( const ObjectTypeCalcer
& ourobj
) const
392 std::vector
<ObjectCalcer
*> parents
= ourobj
.parents();
393 std::set
<ObjectCalcer
*> ret
;
394 std::vector
<ObjectCalcer
*> tmp
= parents
[0]->movableParents();
395 ret
.insert( tmp
.begin(), tmp
.end() );
396 tmp
= parents
[1]->movableParents();
397 ret
.insert( tmp
.begin(), tmp
.end() );
398 ret
.insert( &parents
[0], &parents
[1] );
399 return std::vector
<ObjectCalcer
*>( ret
.begin(), ret
.end() );
402 /* polygon-line intersection */
404 static const ArgsParser::spec argsspecPolygonLineIntersection
[] =
406 { PolygonImp::stype(), I18N_NOOP( "Intersect this polygon with a line" ),
407 I18N_NOOP( "Select the polygon of which you want the intersection with a line..." ), false },
408 { AbstractLineImp::stype(), "Intersect this line with a polygon", "Select the line of which you want the intersection with a polygon...", false }
411 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonLineIntersectionType
)
413 PolygonLineIntersectionType::PolygonLineIntersectionType()
414 : ArgsParserObjectType( "PolygonLineIntersection", argsspecPolygonLineIntersection
, 2 )
418 PolygonLineIntersectionType::~PolygonLineIntersectionType()
422 const PolygonLineIntersectionType
* PolygonLineIntersectionType::instance()
424 static const PolygonLineIntersectionType t
;
429 * Intersection of a polygon and a line/ray/segment.
431 * geometrically speaking the result is always a collection of
432 * collinear nonintersecting open segments (at most one if the
433 * polygon is convex). Since we don't know in advance how many
434 * segments will result, the obvious choice is to return an
435 * InvalidImp in cases when the result is *not* a single segment
437 * computing the two ends of this segment is more tricky then one
438 * expects especially when intersecting segments/rays.
440 * particularly "difficult" situations are those where we intersect
441 * a segment/ray with an/the endpoint coinciding with a vertex of
442 * the polygon, especially if that vertex is a "reentrant" (concave)
443 * vertex of the polygon.
446 ObjectImp
* PolygonLineIntersectionType::calc( const Args
& parents
, const KigDocument
& ) const
448 if ( ! margsparser
.checkArgs( parents
) ) return new InvalidImp
;
450 const PolygonImp
* polygon
= static_cast<const PolygonImp
*>( parents
[0] );
451 const std::vector
<Coordinate
> ppoints
= polygon
->points();
452 const LineData line
= static_cast<const AbstractLineImp
*>( parents
[1] )->data();
453 Coordinate intersections
[2];
454 uint whichintersection
= 0;
456 bool boundleft
= false;
457 bool boundright
= false;
458 if ( parents
[1]->inherits( SegmentImp::stype() ) )
460 boundleft
= boundright
= true;
462 if ( parents
[1]->inherits( RayImp::stype() ) )
466 Coordinate a
= line
.a
;
467 double abx
= line
.b
.x
- a
.x
;
468 double aby
= line
.b
.y
- a
.y
;
470 double leftendinside
= false;
471 double rightendinside
= false;
472 Coordinate prevpoint
= ppoints
.back() - a
;
473 bool prevpointbelow
= ( abx
*prevpoint
.y
<= aby
*prevpoint
.x
);
474 for ( uint i
= 0; i
< ppoints
.size(); ++i
)
476 Coordinate point
= ppoints
[i
] - a
;
477 bool pointbelow
= ( abx
*point
.y
<= aby
*point
.x
);
478 if ( pointbelow
!= prevpointbelow
)
480 /* found an intersection with the support line
481 * compute the value of the parameter...
483 double dcx
= point
.x
- prevpoint
.x
;
484 double dcy
= point
.y
- prevpoint
.y
;
485 double num
= point
.x
*dcy
- point
.y
*dcx
;
486 double den
= abx
*dcy
- aby
*dcx
;
487 if ( std::fabs( den
) <= 1.e
-6*std::fabs( num
) ) continue; //parallel
489 if ( boundleft
&& t
<= 0 )
491 leftendinside
= !leftendinside
;
493 else if ( boundright
&& t
>= 1 )
495 rightendinside
= !rightendinside
;
499 if ( whichintersection
>= 2 ) return new InvalidImp
;
500 intersections
[whichintersection
++] = a
+ t
*Coordinate( abx
, aby
);
504 prevpointbelow
= pointbelow
;
509 if ( whichintersection
>= 2 ) return new InvalidImp
;
510 intersections
[whichintersection
++] = a
;
513 if ( rightendinside
)
515 if ( whichintersection
>= 2 ) return new InvalidImp
;
516 intersections
[whichintersection
++] = line
.b
;
519 switch (whichintersection
)
521 case 1: /* just for completeness: this should never happen */
522 return new PointImp( intersections
[0] );
525 return new SegmentImp( intersections
[0], intersections
[1] );
529 return new InvalidImp
;
534 const ObjectImpType
* PolygonLineIntersectionType::resultId() const
536 return SegmentImp::stype();
539 /* polygon vertices */
541 static const ArgsParser::spec argsspecPolygonVertex
[] =
543 { PolygonImp::stype(), I18N_NOOP( "Construct the vertices of this polygon" ),
544 I18N_NOOP( "Select the polygon of which you want to construct the vertices..." ), true },
545 { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false }
548 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonVertexType
)
550 PolygonVertexType::PolygonVertexType()
551 : ArgsParserObjectType( "PolygonVertex", argsspecPolygonVertex
, 2 )
555 PolygonVertexType::~PolygonVertexType()
559 const PolygonVertexType
* PolygonVertexType::instance()
561 static const PolygonVertexType t
;
565 ObjectImp
* PolygonVertexType::calc( const Args
& parents
, const KigDocument
& ) const
567 if ( ! margsparser
.checkArgs( parents
) ) return new InvalidImp
;
569 const std::vector
<Coordinate
> ppoints
= static_cast<const PolygonImp
*>( parents
[0] )->points();
570 const uint i
= static_cast<const IntImp
*>( parents
[1] )->data();
572 if ( i
>= ppoints
.size() ) return new InvalidImp
;
574 return new PointImp( ppoints
[i
] );
577 const ObjectImpType
* PolygonVertexType::resultId() const
579 return PointImp::stype();
584 static const ArgsParser::spec argsspecPolygonSide
[] =
586 { PolygonImp::stype(), I18N_NOOP( "Construct the sides of this polygon" ),
587 I18N_NOOP( "Select the polygon of which you want to construct the sides..." ), false },
588 { IntImp::stype(), "param", "SHOULD NOT BE SEEN", false }
591 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( PolygonSideType
)
593 PolygonSideType::PolygonSideType()
594 : ArgsParserObjectType( "PolygonSide", argsspecPolygonSide
, 2 )
598 PolygonSideType::~PolygonSideType()
602 const PolygonSideType
* PolygonSideType::instance()
604 static const PolygonSideType t
;
608 ObjectImp
* PolygonSideType::calc( const Args
& parents
, const KigDocument
& ) const
610 if ( ! margsparser
.checkArgs( parents
) ) return new InvalidImp
;
612 const std::vector
<Coordinate
> ppoints
= static_cast<const PolygonImp
*>( parents
[0] )->points();
613 const uint i
= static_cast<const IntImp
*>( parents
[1] )->data();
615 if ( i
>= ppoints
.size() ) return new InvalidImp
;
618 if ( nexti
>= ppoints
.size() ) nexti
= 0;
620 return new SegmentImp( ppoints
[i
], ppoints
[nexti
] );
623 const ObjectImpType
* PolygonSideType::resultId() const
625 return SegmentImp::stype();
628 /* convex hull of a polygon */
630 static const ArgsParser::spec argsspecConvexHull
[] =
632 { PolygonImp::stype(), I18N_NOOP( "Construct the convex hull of this polygon" ),
633 I18N_NOOP( "Select the polygon of which you want to construct the convex hull..." ), false }
636 KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ConvexHullType
)
638 ConvexHullType::ConvexHullType()
639 : ArgsParserObjectType( "ConvexHull", argsspecConvexHull
, 1 )
643 ConvexHullType::~ConvexHullType()
647 const ConvexHullType
* ConvexHullType::instance()
649 static const ConvexHullType t
;
653 ObjectImp
* ConvexHullType::calc( const Args
& parents
, const KigDocument
& ) const
655 if ( ! margsparser
.checkArgs( parents
) ) return new InvalidImp
;
657 const std::vector
<Coordinate
> ppoints
= static_cast<const PolygonImp
*>( parents
[0] )->points();
659 if ( ppoints
.size() < 3 ) return new InvalidImp
;
661 std::vector
<Coordinate
> hull
= computeConvexHull( ppoints
);
662 if ( hull
.size() < 3 ) return new InvalidImp
;
663 return new PolygonImp( hull
);
666 const ObjectImpType
* ConvexHullType::resultId() const
668 return PolygonImp::stype();