1 /*
2     This file is part of Kig, a KDE program for Interactive Geometry...
3     SPDX-FileCopyrightText: 2002 Dominique Devriese <devriese@kde.org>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "kgeo-filter.h"
9 
10 #include "kgeo-resource.h"
11 #include "filters-common.h"
12 
13 #include "../kig/kig_part.h"
14 #include "../kig/kig_document.h"
15 #include "../objects/angle_type.h"
16 #include "../objects/bogus_imp.h"
17 #include "../objects/circle_imp.h"
18 #include "../objects/circle_type.h"
19 #include "../objects/intersection_types.h"
20 #include "../objects/line_type.h"
21 #include "../objects/object_calcer.h"
22 #include "../objects/object_drawer.h"
23 #include "../objects/object_factory.h"
24 #include "../objects/object_holder.h"
25 #include "../objects/other_type.h"
26 #include "../objects/point_imp.h"
27 #include "../objects/point_type.h"
28 #include "../objects/text_type.h"
29 #include "../objects/transform_types.h"
30 #include "../objects/vector_type.h"
31 
32 #include <KConfig>
33 #include <KConfigGroup>
34 
35 #include <algorithm>
36 #include <functional>
37 
supportMime(const QString & mime)38 bool KigFilterKGeo::supportMime( const QString& mime )
39 {
40   return mime == QLatin1String("application/x-kgeo");
41 }
42 
load(const QString & sFrom)43 KigDocument* KigFilterKGeo::load( const QString& sFrom )
44 {
45   // kgeo uses a KConfig to save its contents...
46   KConfig config ( sFrom, KConfig::SimpleConfig );
47 
48   loadMetrics ( &config );
49   return loadObjects ( &config );
50 }
51 
loadMetrics(KConfig * c)52 void KigFilterKGeo::loadMetrics(KConfig* c )
53 {
54   KConfigGroup grp = c->group("Main");
55   xMax = grp.readEntry("XMax", 16);
56   yMax = grp.readEntry("YMax", 11);
57   grid = grp.readEntry( "Grid", true );
58   axes = grp.readEntry( "Axes", true );
59   // the rest is not relevant to us (yet ?)...
60 }
61 
62 struct KGeoHierarchyElement
63 {
64   int id;
65   std::vector<int> parents;
66 };
67 
visitElem(std::vector<KGeoHierarchyElement> & ret,const std::vector<KGeoHierarchyElement> & elems,std::vector<bool> & seen,int i)68 static void visitElem( std::vector<KGeoHierarchyElement>& ret,
69                        const std::vector<KGeoHierarchyElement>& elems,
70                        std::vector<bool>& seen,
71                        int i )
72 {
73   if ( !seen[i] )
74   {
75     for ( uint j = 0; j < elems[i].parents.size(); ++j )
76       visitElem( ret, elems, seen, elems[i].parents[j] );
77     ret.push_back( elems[i] );
78     seen[i] = true;
79   };
80 }
81 
sortElems(const std::vector<KGeoHierarchyElement> & elems)82 static std::vector<KGeoHierarchyElement> sortElems( const std::vector<KGeoHierarchyElement> &elems )
83 {
84   std::vector<KGeoHierarchyElement> ret;
85   std::vector<bool> seenElems( elems.size(), false );
86   for ( uint i = 0; i < elems.size(); ++i )
87     visitElem( ret, elems, seenElems, i );
88   return ret;
89 }
90 
loadObjects(KConfig * c)91 KigDocument* KigFilterKGeo::loadObjects( KConfig* c )
92 {
93   KigDocument* ret = new KigDocument();
94 
95   using namespace std;
96 
97   QString group;
98   bool ok = true;
99   KConfigGroup grp = c->group("Main");
100   int number = grp.readEntry ("Number",0);
101 
102   // first we determine the parent relationships, and sort the
103   // elements in an order that we can be sure all of an object's
104   // parents will have been handled before it is handled itself..
105   // ( aka topological sort of the parent relations graph..
106   std::vector<KGeoHierarchyElement> elems;
107   elems.reserve( number );
108 
109   for ( int i = 0; i < number; ++i )
110   {
111     KGeoHierarchyElement elem;
112     elem.id = i;
113     group.setNum( i + 1 );
114     group.prepend( "Object " );
115 
116     KConfigGroup grp = c->group(group);
117     QStringList parents = grp.readEntry( "Parents", QVariant( QVariant::StringList ) ).toStringList();
118     elems.push_back( elem );
119     for ( QStringList::ConstIterator parent = parents.constBegin(); parent != parents.constEnd(); ++parent )
120     {
121       int parentIndex = ( *parent ).toInt( &ok );
122       if ( ! ok ) KIG_FILTER_PARSE_ERROR;
123       if ( parentIndex != 0 )
124         elems[i].parents.push_back( parentIndex - 1 );
125     };
126   };
127 
128   std::vector<KGeoHierarchyElement> sortedElems = sortElems( elems );
129   std::vector<ObjectHolder*> os;
130   os.resize( number, 0 );
131   const ObjectFactory* factory = ObjectFactory::instance();
132 
133   // now we iterate over the elems again in the newly determined
134   // order..
135   for ( uint i = 0; i < sortedElems.size(); ++i )
136   {
137     const KGeoHierarchyElement& e = sortedElems[i];
138     int id = e.id;
139     group.setNum( id + 1 );
140     group.prepend( "Object " );
141     KConfigGroup grp = c->group(group );
142     int objID = grp.readEntry( "Geo",0 );
143 
144     std::vector<ObjectCalcer*> parents;
145     for ( uint j = 0; j < e.parents.size(); ++j )
146     {
147       int parentid = e.parents[j];
148       parents.push_back( os[parentid]->calcer() );
149     };
150 
151     ObjectCalcer* o = 0;
152     switch (objID)
153     {
154     case ID_point:
155     {
156       // fetch the coordinates...
157       QString strX = grp.readEntry("QPointX");
158       QString strY = grp.readEntry("QPointY");
159       double x = strX.toDouble(&ok);
160       if (!ok) KIG_FILTER_PARSE_ERROR;
161       double y = strY.toDouble(&ok);
162       if (!ok) KIG_FILTER_PARSE_ERROR;
163       Coordinate m( x, y );
164       uint nparents = parents.size();
165       if ( nparents == 0 )
166         o = factory->fixedPointCalcer( m );
167       else if ( nparents == 1 )
168         o = factory->constrainedPointCalcer( parents[0], m, *ret );
169       else
170         KIG_FILTER_PARSE_ERROR;
171       break;
172     }
173     case ID_segment:
174     {
175       o = new ObjectTypeCalcer( SegmentABType::instance(), parents );
176       break;
177     }
178     case ID_circle:
179     {
180       o = new ObjectTypeCalcer( CircleBCPType::instance(), parents );
181       break;
182     }
183     case ID_line:
184     {
185       o = new ObjectTypeCalcer( LineABType::instance(), parents );
186       break;
187     }
188     case ID_bisection:
189     {
190       // if this is the bisection of two points, first build a segment
191       // between them..
192       if ( parents.size() == 2 )
193       {
194         ObjectTypeCalcer* seg = new ObjectTypeCalcer( SegmentABType::instance(), parents );
195         parents.clear();
196         parents.push_back( seg );
197       }
198       if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
199       o = factory->propertyObjectCalcer( parents[0], "mid-point" );
200       break;
201     };
202     case ID_perpendicular:
203     {
204       o = new ObjectTypeCalcer( LinePerpendLPType::instance(), parents );
205       break;
206     }
207     case ID_parallel:
208     {
209       o = new ObjectTypeCalcer( LineParallelLPType::instance(), parents );
210       break;
211     }
212     case ID_vector:
213     {
214       o = new ObjectTypeCalcer( VectorType::instance(), parents );
215       break;
216     }
217     case ID_ray:
218     {
219       o = new ObjectTypeCalcer( RayABType::instance(), parents );
220       break;
221     }
222     case ID_move:
223     {
224       o = new ObjectTypeCalcer( TranslatedType::instance(), parents );
225       break;
226     }
227     case ID_mirrorPoint:
228     {
229       o = new ObjectTypeCalcer( PointReflectionType::instance(), parents );
230       break;
231     }
232     case ID_pointOfConc:
233     {
234       o = new ObjectTypeCalcer( LineLineIntersectionType::instance(), parents );
235       break;
236     }
237     case ID_text:
238     {
239       bool frame = grp.readEntry( "Frame",true );
240       double x = grp.readEntry( "TextRectCenterX",0.0 );
241       double y = grp.readEntry( "TextRectCenterY",0.0 );
242       QString text = grp.readEntry( "TextRectEntry" );
243       double height = grp.readEntry( "TextRectHeight",0.0 );
244       double width  = grp.readEntry( "TextRectWidth",0.0 );
245       // we don't want the center, but the top left..
246       x -= width / 80;
247       y -= height / 80;
248       o = factory->labelCalcer(
249         text, Coordinate( x, y ), frame, std::vector<ObjectCalcer*>(), *ret );
250       break;
251     }
252     case ID_fixedCircle:
253     {
254       double r = grp.readEntry( "Radius",0.0 );
255       parents.push_back( new ObjectConstCalcer( new DoubleImp( r ) ) );
256       o = new ObjectTypeCalcer( CircleBPRType::instance(), parents );
257       break;
258     }
259     case ID_angle:
260     {
261       if ( parents.size() == 3 )
262       {
263         ObjectTypeCalcer* ao = new ObjectTypeCalcer( AngleType::instance(), parents );
264         ao->calc( *ret );
265         parents.clear();
266         parents.push_back( ao );
267       };
268       if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
269       ObjectCalcer* angle = parents[0];
270       parents.clear();
271       const Coordinate c =
272         static_cast<const PointImp*>( angle->parents()[1]->imp() )->coordinate();
273       o = filtersConstructTextObject( c, angle, "angle-degrees", *ret, true );
274       break;
275     }
276     case ID_distance:
277     {
278       if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR;
279       ObjectTypeCalcer* segment = new ObjectTypeCalcer( SegmentABType::instance(), parents );
280       segment->calc( *ret );
281       Coordinate m = ( static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
282                        static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
283       o = filtersConstructTextObject( m, segment, "length", *ret, true );
284       break;
285     }
286     case ID_arc:
287     {
288       o = new ObjectTypeCalcer( AngleType::instance(), parents );
289       break;
290     }
291     case ID_area:
292     {
293       if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
294       const CircleImp* circle = static_cast<const CircleImp*>( parents[0]->imp() );
295       const Coordinate c = circle->center() + Coordinate( circle->radius(), 0 );
296       o = filtersConstructTextObject( c, parents[0], "surface", *ret, true );
297       break;
298     }
299     case ID_slope:
300     {
301       // if parents contains a segment, line, vector or whatever, we
302       // take its parents cause we want points..
303       if ( parents.size() == 1 ) parents = parents[0]->parents();
304       if ( parents.size() != 2 ) KIG_FILTER_PARSE_ERROR;
305       const Coordinate c = (
306         static_cast<const PointImp*>( parents[0]->imp() )->coordinate() +
307         static_cast<const PointImp*>( parents[1]->imp() )->coordinate() ) / 2;
308       ObjectTypeCalcer* line = new ObjectTypeCalcer( LineABType::instance(), parents );
309       line->calc( *ret );
310       o = filtersConstructTextObject( c, line, "slope", *ret, true );
311       break;
312     }
313     case ID_circumference:
314     {
315       if ( parents.size() != 1 ) KIG_FILTER_PARSE_ERROR;
316       const CircleImp* c = static_cast<const CircleImp*>( parents[0]->imp() );
317       const Coordinate m = c->center() + Coordinate( c->radius(), 0 );
318       o = filtersConstructTextObject( m, parents[0], "circumference", *ret, true );
319       break;
320     }
321     case ID_rotation:
322     {
323       // in kig, the rotated object should be last..
324       ObjectCalcer* t = parents[2];
325       parents[2] = parents[0];
326       parents[0] = t;
327       o = new ObjectTypeCalcer( RotationType::instance(), parents );
328       break;
329     }
330     default:
331       KIG_FILTER_PARSE_ERROR;
332     };
333 
334     // set the color...
335     QColor co = grp.readEntry( "Color",QColor() );
336     if( !co.isValid() )
337       co = Qt::blue;
338     ObjectDrawer* d = new ObjectDrawer( co );
339 
340     os[i] = new ObjectHolder( o, d );
341     os[i]->calc( *ret );
342   }; // for loop (creating KGeoHierarchyElements..
343 
344   ret->addObjects( os );
345   ret->setGrid( grid );
346   ret->setAxes( axes );
347   return ret;
348 }
349 
KigFilterKGeo()350 KigFilterKGeo::KigFilterKGeo()
351 {
352 }
353 
~KigFilterKGeo()354 KigFilterKGeo::~KigFilterKGeo()
355 {
356 }
357 
instance()358 KigFilterKGeo* KigFilterKGeo::instance()
359 {
360   static KigFilterKGeo f;
361   return &f;
362 }
363