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