Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgssymbolv2.cpp 00003 --------------------- 00004 begin : November 2009 00005 copyright : (C) 2009 by Martin Dobias 00006 email : wonder.sk at gmail.com 00007 *************************************************************************** 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 ***************************************************************************/ 00015 00016 #include "qgsrenderer.h" 00017 #include "qgssymbolv2.h" 00018 #include "qgssymbollayerv2.h" 00019 00020 #include "qgslinesymbollayerv2.h" 00021 #include "qgsmarkersymbollayerv2.h" 00022 #include "qgsfillsymbollayerv2.h" 00023 00024 #include "qgslogger.h" 00025 #include "qgsrendercontext.h" // for bigSymbolPreview 00026 00027 #include <QColor> 00028 #include <QImage> 00029 #include <QPainter> 00030 #include <QSize> 00031 00032 #include <cmath> 00033 00034 QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers ) 00035 : mType( type ), mLayers( layers ), mOutputUnit( MM ), mAlpha( 1.0 ), mRenderHints( 0 ) 00036 { 00037 00038 // check they're all correct symbol layers 00039 for ( int i = 0; i < mLayers.count(); i++ ) 00040 { 00041 if ( mLayers[i] == NULL ) 00042 { 00043 mLayers.removeAt( i-- ); 00044 } 00045 else if ( !isSymbolLayerCompatible( mLayers[i]->type() ) ) 00046 { 00047 delete mLayers[i]; 00048 mLayers.removeAt( i-- ); 00049 } 00050 } 00051 00052 } 00053 00054 QgsSymbolV2::~QgsSymbolV2() 00055 { 00056 // delete all symbol layers (we own them, so it's okay) 00057 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00058 delete *it; 00059 } 00060 00061 QgsSymbolV2* QgsSymbolV2::defaultSymbol( QGis::GeometryType geomType ) 00062 { 00063 QgsSymbolV2* s; 00064 switch ( geomType ) 00065 { 00066 case QGis::Point: s = new QgsMarkerSymbolV2(); break; 00067 case QGis::Line: s = new QgsLineSymbolV2(); break; 00068 case QGis::Polygon: s = new QgsFillSymbolV2(); break; 00069 default: QgsDebugMsg( "unknown layer's geometry type" ); return NULL; 00070 } 00071 00072 s->setColor( QColor::fromHsv( rand() % 360, 64 + rand() % 192, 128 + rand() % 128 ) ); 00073 return s; 00074 } 00075 00076 00077 QgsSymbolLayerV2* QgsSymbolV2::symbolLayer( int layer ) 00078 { 00079 if ( layer < 0 || layer >= mLayers.count() ) 00080 return NULL; 00081 00082 return mLayers[layer]; 00083 } 00084 00085 00086 bool QgsSymbolV2::isSymbolLayerCompatible( SymbolType t ) 00087 { 00088 // fill symbol can contain also line symbol layers for drawing of outlines 00089 if ( mType == Fill && t == Line ) 00090 return true; 00091 00092 return mType == t; 00093 } 00094 00095 00096 bool QgsSymbolV2::insertSymbolLayer( int index, QgsSymbolLayerV2* layer ) 00097 { 00098 if ( index < 0 || index > mLayers.count() ) // can be added also after the last index 00099 return false; 00100 if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) ) 00101 return false; 00102 00103 mLayers.insert( index, layer ); 00104 return true; 00105 } 00106 00107 00108 bool QgsSymbolV2::appendSymbolLayer( QgsSymbolLayerV2* layer ) 00109 { 00110 if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) ) 00111 return false; 00112 00113 mLayers.append( layer ); 00114 return true; 00115 } 00116 00117 00118 bool QgsSymbolV2::deleteSymbolLayer( int index ) 00119 { 00120 if ( index < 0 || index >= mLayers.count() ) 00121 return false; 00122 00123 delete mLayers[index]; 00124 mLayers.removeAt( index ); 00125 return true; 00126 } 00127 00128 00129 QgsSymbolLayerV2* QgsSymbolV2::takeSymbolLayer( int index ) 00130 { 00131 if ( index < 0 || index >= mLayers.count() ) 00132 return NULL; 00133 00134 return mLayers.takeAt( index ); 00135 } 00136 00137 00138 bool QgsSymbolV2::changeSymbolLayer( int index, QgsSymbolLayerV2* layer ) 00139 { 00140 if ( index < 0 || index >= mLayers.count() ) 00141 return false; 00142 if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) ) 00143 return false; 00144 00145 delete mLayers[index]; // first delete the original layer 00146 mLayers[index] = layer; // set new layer 00147 return true; 00148 } 00149 00150 00151 void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsVectorLayer* layer ) 00152 { 00153 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints ); 00154 symbolContext.setLayer( layer ); 00155 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00156 ( *it )->startRender( symbolContext ); 00157 } 00158 00159 void QgsSymbolV2::stopRender( QgsRenderContext& context ) 00160 { 00161 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints ); 00162 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00163 ( *it )->stopRender( symbolContext ); 00164 } 00165 00166 void QgsSymbolV2::setColor( const QColor& color ) 00167 { 00168 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00169 { 00170 if ( !( *it )->isLocked() ) 00171 ( *it )->setColor( color ); 00172 } 00173 } 00174 00175 QColor QgsSymbolV2::color() 00176 { 00177 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00178 { 00179 // return color of the first unlocked layer 00180 if ( !( *it )->isLocked() ) 00181 return ( *it )->color(); 00182 } 00183 return QColor( 0, 0, 0 ); 00184 } 00185 00186 void QgsSymbolV2::drawPreviewIcon( QPainter* painter, QSize size ) 00187 { 00188 QgsRenderContext context = QgsSymbolLayerV2Utils::createRenderContext( painter ); 00189 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints ); 00190 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00191 { 00192 if ( mType == Fill && ( *it )->type() == Line ) 00193 { 00194 // line symbol layer would normally draw just a line 00195 // so we override this case to force it to draw a polygon outline 00196 QgsLineSymbolLayerV2* lsl = ( QgsLineSymbolLayerV2* ) * it; 00197 00198 // from QgsFillSymbolLayerV2::drawPreviewIcon() 00199 QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width() - 1, size.height() - 1 ) ); 00200 lsl->startRender( symbolContext ); 00201 lsl->renderPolygonOutline( poly, NULL, symbolContext ); 00202 lsl->stopRender( symbolContext ); 00203 } 00204 else 00205 ( *it )->drawPreviewIcon( symbolContext, size ); 00206 } 00207 } 00208 00209 00210 QImage QgsSymbolV2::bigSymbolPreviewImage() 00211 { 00212 QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied ); 00213 preview.fill( 0 ); 00214 00215 QPainter p( &preview ); 00216 p.setRenderHint( QPainter::Antialiasing ); 00217 p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialising 00218 00219 if ( mType == QgsSymbolV2::Marker ) 00220 { 00221 p.setPen( QPen( Qt::gray ) ); 00222 p.drawLine( 0, 50, 100, 50 ); 00223 p.drawLine( 50, 0, 50, 100 ); 00224 } 00225 00226 QgsRenderContext context = QgsSymbolLayerV2Utils::createRenderContext( &p ); 00227 startRender( context ); 00228 00229 if ( mType == QgsSymbolV2::Line ) 00230 { 00231 QPolygonF poly; 00232 poly << QPointF( 0, 50 ) << QPointF( 99, 50 ); 00233 static_cast<QgsLineSymbolV2*>( this )->renderPolyline( poly, 0, context ); 00234 } 00235 else if ( mType == QgsSymbolV2::Fill ) 00236 { 00237 QPolygonF polygon; 00238 polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 ); 00239 static_cast<QgsFillSymbolV2*>( this )->renderPolygon( polygon, NULL, 0, context ); 00240 } 00241 else // marker 00242 { 00243 static_cast<QgsMarkerSymbolV2*>( this )->renderPoint( QPointF( 50, 50 ), 0, context ); 00244 } 00245 00246 stopRender( context ); 00247 return preview; 00248 } 00249 00250 00251 QString QgsSymbolV2::dump() 00252 { 00253 QString t; 00254 switch ( type() ) 00255 { 00256 case QgsSymbolV2::Marker: t = "MARKER"; break; 00257 case QgsSymbolV2::Line: t = "LINE"; break; 00258 case QgsSymbolV2::Fill: t = "FILL"; break; 00259 default: Q_ASSERT( 0 && "unknown symbol type" ); 00260 } 00261 QString s = QString( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerV2Utils::encodeColor( color() ) ); 00262 00263 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00264 { 00265 // TODO: 00266 } 00267 return s; 00268 } 00269 00270 void QgsSymbolV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00271 { 00272 props[ "alpha" ] = QString::number( alpha() ); 00273 double scaleFactor = 1.0; 00274 props[ "uom" ] = QgsSymbolLayerV2Utils::encodeSldUom( outputUnit(), &scaleFactor ); 00275 props[ "uomScale" ] = scaleFactor != 1 ? QString::number( scaleFactor ) : ""; 00276 00277 for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00278 { 00279 ( *it )->toSld( doc, element, props ); 00280 } 00281 } 00282 00283 QgsSymbolLayerV2List QgsSymbolV2::cloneLayers() const 00284 { 00285 QgsSymbolLayerV2List lst; 00286 for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00287 { 00288 QgsSymbolLayerV2* layer = ( *it )->clone(); 00289 layer->setLocked(( *it )->isLocked() ); 00290 layer->setRenderingPass(( *it )->renderingPass() ); 00291 lst.append( layer ); 00292 } 00293 return lst; 00294 } 00295 00296 QSet<QString> QgsSymbolV2::usedAttributes() const 00297 { 00298 QSet<QString> attributes; 00299 QgsSymbolLayerV2List::const_iterator sIt = mLayers.constBegin(); 00300 for ( ; sIt != mLayers.constEnd(); ++sIt ) 00301 { 00302 if ( *sIt ) 00303 { 00304 attributes.unite(( *sIt )->usedAttributes() ); 00305 } 00306 } 00307 return attributes; 00308 } 00309 00311 00312 QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected, int renderHints, const QgsFeature* f ) 00313 : mRenderContext( c ), mOutputUnit( u ), mAlpha( alpha ), mSelected( selected ), mRenderHints( renderHints ), mFeature( f ), mLayer( 0 ) 00314 { 00315 00316 } 00317 00318 QgsSymbolV2RenderContext::~QgsSymbolV2RenderContext() 00319 { 00320 00321 } 00322 00323 QColor QgsSymbolV2RenderContext::selectionColor() 00324 { 00325 return QgsRenderer::selectionColor(); 00326 } 00327 00328 00329 double QgsSymbolV2RenderContext::outputLineWidth( double width ) const 00330 { 00331 return width * QgsSymbolLayerV2Utils::lineWidthScaleFactor( mRenderContext, mOutputUnit ); 00332 } 00333 00334 double QgsSymbolV2RenderContext::outputPixelSize( double size ) const 00335 { 00336 return size * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( mRenderContext, mOutputUnit ); 00337 } 00338 00339 QgsSymbolV2RenderContext& QgsSymbolV2RenderContext::operator=( const QgsSymbolV2RenderContext& ) 00340 { 00341 // This is just a dummy implementation of assignment. 00342 // sip 4.7 generates a piece of code that needs this function to exist. 00343 // It's not generated automatically by the compiler because of 00344 // mRenderContext member which is a reference (and thus can't be changed). 00345 Q_ASSERT( false ); 00346 return *this; 00347 } 00348 00350 00351 QgsMarkerSymbolV2* QgsMarkerSymbolV2::createSimple( const QgsStringMap& properties ) 00352 { 00353 QgsSymbolLayerV2* sl = QgsSimpleMarkerSymbolLayerV2::create( properties ); 00354 if ( sl == NULL ) 00355 return NULL; 00356 00357 QgsSymbolLayerV2List layers; 00358 layers.append( sl ); 00359 return new QgsMarkerSymbolV2( layers ); 00360 } 00361 00362 QgsLineSymbolV2* QgsLineSymbolV2::createSimple( const QgsStringMap& properties ) 00363 { 00364 QgsSymbolLayerV2* sl = QgsSimpleLineSymbolLayerV2::create( properties ); 00365 if ( sl == NULL ) 00366 return NULL; 00367 00368 QgsSymbolLayerV2List layers; 00369 layers.append( sl ); 00370 return new QgsLineSymbolV2( layers ); 00371 } 00372 00373 QgsFillSymbolV2* QgsFillSymbolV2::createSimple( const QgsStringMap& properties ) 00374 { 00375 QgsSymbolLayerV2* sl = QgsSimpleFillSymbolLayerV2::create( properties ); 00376 if ( sl == NULL ) 00377 return NULL; 00378 00379 QgsSymbolLayerV2List layers; 00380 layers.append( sl ); 00381 return new QgsFillSymbolV2( layers ); 00382 } 00383 00385 00386 00387 QgsMarkerSymbolV2::QgsMarkerSymbolV2( QgsSymbolLayerV2List layers ) 00388 : QgsSymbolV2( Marker, layers ) 00389 { 00390 if ( mLayers.count() == 0 ) 00391 mLayers.append( new QgsSimpleMarkerSymbolLayerV2() ); 00392 } 00393 00394 void QgsMarkerSymbolV2::setAngle( double angle ) 00395 { 00396 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00397 { 00398 QgsMarkerSymbolLayerV2* layer = ( QgsMarkerSymbolLayerV2* ) * it; 00399 layer->setAngle( angle ); 00400 } 00401 } 00402 00403 double QgsMarkerSymbolV2::angle() 00404 { 00405 QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); 00406 00407 if ( it == mLayers.end() ) 00408 return 0; 00409 00410 // return angle of the first symbol layer 00411 const QgsMarkerSymbolLayerV2 *layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it ); 00412 return layer->angle(); 00413 } 00414 00415 void QgsMarkerSymbolV2::setSize( double s ) 00416 { 00417 double origSize = size(); 00418 00419 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00420 { 00421 QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2*>( *it ); 00422 if ( layer->size() == origSize ) 00423 layer->setSize( s ); 00424 else 00425 { 00426 // proportionally scale size 00427 if ( origSize != 0 ) 00428 layer->setSize( layer->size() * s / origSize ); 00429 } 00430 } 00431 } 00432 00433 double QgsMarkerSymbolV2::size() 00434 { 00435 // return size of the largest symbol 00436 double maxSize = 0; 00437 for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00438 { 00439 const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it ); 00440 double lsize = layer->size(); 00441 if ( lsize > maxSize ) 00442 maxSize = lsize; 00443 } 00444 return maxSize; 00445 } 00446 00447 void QgsMarkerSymbolV2::renderPoint( const QPointF& point, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected ) 00448 { 00449 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f ); 00450 if ( layer != -1 ) 00451 { 00452 if ( layer >= 0 && layer < mLayers.count() ) 00453 (( QgsMarkerSymbolLayerV2* ) mLayers[layer] )->renderPoint( point, symbolContext ); 00454 return; 00455 } 00456 00457 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00458 { 00459 QgsMarkerSymbolLayerV2* layer = ( QgsMarkerSymbolLayerV2* ) * it; 00460 layer->renderPoint( point, symbolContext ); 00461 } 00462 } 00463 00464 QgsSymbolV2* QgsMarkerSymbolV2::clone() const 00465 { 00466 QgsSymbolV2* cloneSymbol = new QgsMarkerSymbolV2( cloneLayers() ); 00467 cloneSymbol->setOutputUnit( mOutputUnit ); 00468 cloneSymbol->setAlpha( mAlpha ); 00469 return cloneSymbol; 00470 } 00471 00472 00474 // LINE 00475 00476 QgsLineSymbolV2::QgsLineSymbolV2( QgsSymbolLayerV2List layers ) 00477 : QgsSymbolV2( Line, layers ) 00478 { 00479 if ( mLayers.count() == 0 ) 00480 mLayers.append( new QgsSimpleLineSymbolLayerV2() ); 00481 } 00482 00483 void QgsLineSymbolV2::setWidth( double w ) 00484 { 00485 double origWidth = width(); 00486 00487 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00488 { 00489 QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it; 00490 if ( layer->width() == origWidth ) 00491 { 00492 layer->setWidth( w ); 00493 } 00494 else 00495 { 00496 // proportionally scale the width 00497 if ( origWidth != 0 ) 00498 layer->setWidth( layer->width() * w / origWidth ); 00499 } 00500 } 00501 } 00502 00503 double QgsLineSymbolV2::width() 00504 { 00505 double maxWidth = 0; 00506 for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00507 { 00508 const QgsLineSymbolLayerV2* layer = ( const QgsLineSymbolLayerV2* ) * it; 00509 double width = layer->width(); 00510 if ( width > maxWidth ) 00511 maxWidth = width; 00512 } 00513 return maxWidth; 00514 } 00515 00516 void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected ) 00517 { 00518 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f ); 00519 if ( layer != -1 ) 00520 { 00521 if ( layer >= 0 && layer < mLayers.count() ) 00522 (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolyline( points, symbolContext ); 00523 return; 00524 } 00525 00526 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00527 { 00528 QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it; 00529 layer->renderPolyline( points, symbolContext ); 00530 } 00531 } 00532 00533 00534 QgsSymbolV2* QgsLineSymbolV2::clone() const 00535 { 00536 QgsSymbolV2* cloneSymbol = new QgsLineSymbolV2( cloneLayers() ); 00537 cloneSymbol->setOutputUnit( mOutputUnit ); 00538 cloneSymbol->setAlpha( mAlpha ); 00539 return cloneSymbol; 00540 } 00541 00543 // FILL 00544 00545 QgsFillSymbolV2::QgsFillSymbolV2( QgsSymbolLayerV2List layers ) 00546 : QgsSymbolV2( Fill, layers ) 00547 { 00548 if ( mLayers.count() == 0 ) 00549 mLayers.append( new QgsSimpleFillSymbolLayerV2() ); 00550 } 00551 00552 void QgsFillSymbolV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected ) 00553 { 00554 QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f ); 00555 if ( layer != -1 ) 00556 { 00557 if ( layer >= 0 && layer < mLayers.count() ) 00558 { 00559 QgsSymbolV2::SymbolType layertype = mLayers.at( layer )->type(); 00560 if ( layertype == QgsSymbolV2::Fill ) 00561 (( QgsFillSymbolLayerV2* ) mLayers[layer] )->renderPolygon( points, rings, symbolContext ); 00562 else if ( layertype == QgsSymbolV2::Line ) 00563 (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolygonOutline( points, rings, symbolContext ); 00564 } 00565 return; 00566 } 00567 00568 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00569 { 00570 QgsSymbolV2::SymbolType layertype = ( *it )->type(); 00571 if ( layertype == QgsSymbolV2::Fill ) 00572 { 00573 QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it; 00574 layer->renderPolygon( points, rings, symbolContext ); 00575 } 00576 else if ( layertype == QgsSymbolV2::Line ) 00577 { 00578 QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it; 00579 layer->renderPolygonOutline( points, rings, symbolContext ); 00580 } 00581 } 00582 } 00583 00584 00585 QgsSymbolV2* QgsFillSymbolV2::clone() const 00586 { 00587 QgsSymbolV2* cloneSymbol = new QgsFillSymbolV2( cloneLayers() ); 00588 cloneSymbol->setOutputUnit( mOutputUnit ); 00589 cloneSymbol->setAlpha( mAlpha ); 00590 return cloneSymbol; 00591 } 00592 00593 void QgsFillSymbolV2::setAngle( double angle ) 00594 { 00595 for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it ) 00596 { 00597 QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it; 00598 layer->setAngle( angle ); 00599 } 00600 }