You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kpresenter/KPrPieObject.cpp

608 lines
20 KiB

/* This file is part of the KDE project
Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
Copyright (C) 2005-2006 Thorsten Zachmann <zachmann@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KPrPieObject.h"
#include "KPrGradient.h"
#include "KPrUtils.h"
#include "KPrPieObjectIface.h"
#include <KoTextZoomHandler.h>
#include <KoOasisContext.h>
#include <KoStyleStack.h>
#include <KoXmlNS.h>
#include <kdebug.h>
#include <tqregion.h>
#include <tqpicture.h>
#include <tqdom.h>
#include <tqpainter.h>
#include <tqbitmap.h>
using namespace std;
KPrPieObject::KPrPieObject()
: KPr2DObject()
, KPrStartEndLine( L_NORMAL, L_NORMAL )
{
pieType = PT_PIE;
p_angle = 45 * 16;
p_len = 270 * 16;
}
KPrPieObject::KPrPieObject( const KoPen &_pen, const TQBrush &_brush, FillType _fillType,
const TQColor &_gColor1, const TQColor &_gColor2, BCType _gType,
PieType _pieType, int _p_angle, int _p_len,
LineEnd _lineBegin, LineEnd _lineEnd,
bool _unbalanced, int _xfactor, int _yfactor )
: KPr2DObject( _pen, _brush, _fillType, _gColor1, _gColor2, _gType, _unbalanced, _xfactor, _yfactor )
, KPrStartEndLine( _lineBegin, _lineEnd )
{
pieType = _pieType;
p_angle = _p_angle;
p_len = _p_len;
}
DCOPObject* KPrPieObject::dcopObject()
{
if ( !dcop )
dcop = new KPrPieObjectIface( this );
return dcop;
}
KPrPieObject &KPrPieObject::operator=( const KPrPieObject & )
{
return *this;
}
TQDomDocumentFragment KPrPieObject::save( TQDomDocument& doc, double offset )
{
TQDomDocumentFragment fragment=KPr2DObject::save(doc, offset);
KPrStartEndLine::save( fragment, doc );
if (p_angle!=720)
fragment.appendChild(KPrObject::createValueElement("PIEANGLE", p_angle, doc));
if (p_len!=1440)
fragment.appendChild(KPrObject::createValueElement("PIELENGTH", p_len, doc));
if (pieType!=PT_PIE)
fragment.appendChild(KPrObject::createValueElement("PIETYPE", static_cast<int>(pieType), doc));
return fragment;
}
bool KPrPieObject::saveOasisObjectAttributes( KPOasisSaveContext &sc ) const
{
switch( pieType )
{
case PT_PIE:
sc.xmlWriter.addAttribute( "draw:kind", "section" );
break;
case PT_CHORD:
sc.xmlWriter.addAttribute( "draw:kind", "cut" );
break;
case PT_ARC:
sc.xmlWriter.addAttribute( "draw:kind", "arc" );
break;
default:
kdDebug() << " type of pie not supported" << endl;
}
int startangle = ( (int)p_angle / 16 );
sc.xmlWriter.addAttribute( "draw:start-angle", startangle );
int endangle = ( (int) p_len / 16 ) + startangle;
sc.xmlWriter.addAttribute( "draw:end-angle", endangle );
return true;
}
void KPrPieObject::fillStyle( KoGenStyle& styleObjectAuto, KoGenStyles& mainStyles ) const
{
KPrShadowObject::fillStyle( styleObjectAuto, mainStyles );
if ( pieType == PT_ARC )
{
saveOasisMarkerElement( mainStyles, styleObjectAuto );
}
else
{
m_brush.saveOasisFillStyle( styleObjectAuto, mainStyles );
}
}
const char * KPrPieObject::getOasisElementName() const
{
return ext.width() == ext.height() ? "draw:circle" : "draw:ellipse";
}
void KPrPieObject::loadOasis(const TQDomElement &element, KoOasisContext & context, KPrLoadingInfo *info)
{
kdDebug()<<"void KPrPieObject::loadOasis(const TQDomElement &element) ***************\n";
KPr2DObject::loadOasis(element, context, info);
TQString kind = element.attributeNS( KoXmlNS::draw, "kind", TQString() );
if ( kind == "section" )
pieType = PT_PIE;
else if ( kind == "cut" )
pieType = PT_CHORD;
else if ( kind == "arc" )
pieType =PT_ARC;
else
{
kdDebug()<<" KPrPieObject::loadOasis(const TQDomElement &element) type indefined :"<<kind<<endl;
pieType = PT_PIE;
}
kdDebug()<<" type of pie object :"<<( ( pieType == PT_PIE ) ? "pie" : ( pieType == PT_CHORD )?"cut" : "arc" )<<endl;
int start = (int) ( element.attributeNS( KoXmlNS::draw, "start-angle", TQString() ).toDouble() );
p_angle=start*16;
int end = (int) ( element.attributeNS( KoXmlNS::draw, "end-angle", TQString() ).toDouble() );
if ( end < start )
p_len = ( ( 360 - start + end ) * 16 );
else
p_len = ( ( end - start ) * 16 );
kdDebug()<<"KPrPieObject::loadOasis(const TQDomElement &element) : p_angle :"<<p_angle<<" p_len :"<<p_len<<endl;
if ( pieType == PT_ARC )
{
loadOasisMarkerElement( context, "marker-start", lineBegin );
loadOasisMarkerElement( context, "marker-end", lineEnd );
}
}
double KPrPieObject::load(const TQDomElement &element)
{
double offset=KPr2DObject::load(element);
KPrStartEndLine::load( element );
TQDomElement e=element.namedItem("PIEANGLE").toElement();
if(!e.isNull()) {
int tmp=0;
if(e.hasAttribute("value"))
tmp=e.attribute("value").toInt();
p_angle=tmp;
}
e=element.namedItem("PIELENGTH").toElement();
if(!e.isNull()) {
int tmp=0;
if(e.hasAttribute("value"))
tmp=e.attribute("value").toInt();
p_len=tmp;
}
else
p_len=1440; //necessary to reinitialise p_len
//I don't know who change default value
e=element.namedItem("PIETYPE").toElement();
if(!e.isNull()) {
int tmp=0;
if(e.hasAttribute("value"))
tmp=e.attribute("value").toInt();
pieType=static_cast<PieType>(tmp);
}
return offset;
}
void KPrPieObject::paint( TQPainter* _painter, KoTextZoomHandler*_zoomHandler,
int /* pageNum */, bool drawingShadow, bool drawContour )
{
double ow = ext.width();
double oh = ext.height();
double pw = ( ( pen.style() == TQt::NoPen ) ? 1 : pen.pointWidth() ) / 2.0;
if ( drawContour ) {
TQPen pen3( TQt::black, 1, TQt::DotLine );
_painter->setPen( pen3 );
_painter->setRasterOp( TQt::NotXorROP );
}
else {
TQPen pen2 = pen.zoomedPen( _zoomHandler );
_painter->setPen( pen2 );
if ( drawingShadow || getFillType() == FT_BRUSH || !gradient )
{
_painter->setBrush( getBrush() );
}
else
{
if ( pieType != PT_ARC )
{
TQSize size( _zoomHandler->zoomSize( ext ) );
if ( m_redrawGradientPix || gradient->size() != size )
{
m_redrawGradientPix = false;
gradient->setSize( size );
m_gradientPix.resize ( size );
m_gradientPix.fill( TQt::white );
TQPainter p;
p.begin( &m_gradientPix );
p.drawPixmap( 0, 0, gradient->pixmap() );
p.end();
TQBitmap mask( size, true );
p.begin( &mask );
p.setPen( TQPen( TQt::color1 ) );
p.setBrush( TQBrush( TQt::color1 ) );
if ( pieType == PT_CHORD )
{
p.drawChord( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
_zoomHandler->zoomItX(ow - 2 * pw),
_zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
}
else
{
p.drawPie( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
_zoomHandler->zoomItX( ow - 2 * pw),
_zoomHandler->zoomItY( oh - 2 * pw), p_angle, p_len );
}
p.end();
m_gradientPix.setMask( mask );
}
_painter->drawPixmap( 0, 0, m_gradientPix, 0, 0, size.width(), size.height() );
_painter->setBrush( TQt::NoBrush );
}
}
if ( pieType == PT_ARC )
{
KoPointArray points( 2 );
setEndPoints( points );
KoPoint start( points.point( 0 ) );
KoPoint end( points.point( 1 ) );
double ys = ( ( 1 - start.x() / ( ext.width() * ext.width() / 4 ) ) * ext.height() * ext.height() / 4 ) / start.y();
double s_angle = 90 + ( atan( ( start.x() - 1 ) / ( start.y() - ys ) ) * 180 / M_PI );
if ( p_angle / 16 >= 90 && p_angle / 16 <= 270 )
{
s_angle += 180.0;
}
double ye = ( ( 1 - end.x() / ( ext.width() * ext.width() / 4 ) ) * ext.height() * ext.height() / 4 ) / end.y();
double e_angle = 270 + ( atan( ( end.x() - 1 ) / ( end.y() - ye ) ) * 180 / M_PI );
if ( ( ( p_angle + p_len ) / 16 ) % 360 >= 90 && ( ( p_angle + p_len ) / 16 ) % 360 <= 270 )
{
e_angle -= 180.0;
}
start = KoPoint( ext.width() / 2.0 + start.x(), ext.height() / 2.0 - start.y() );
end = KoPoint( ext.width() / 2.0 + end.x(), ext.height() / 2.0 - end.y() );
if ( lineBegin != L_NORMAL )
drawFigureWithOffset( lineBegin, _painter, start,
pen2.color(), int( pen.pointWidth() ), s_angle, _zoomHandler, true );
if ( lineEnd != L_NORMAL )
drawFigureWithOffset( lineEnd, _painter, end,
pen2.color(), int( pen.pointWidth() ), e_angle, _zoomHandler, false );
}
}
switch ( pieType )
{
case PT_PIE:
_painter->drawPie( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY( pw),
_zoomHandler->zoomItX( ow - 2 * pw),
_zoomHandler->zoomItY( oh - 2 * pw), p_angle, p_len );
break;
case PT_ARC:
_painter->drawArc( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
_zoomHandler->zoomItX(ow - 2 * pw),
_zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
break;
case PT_CHORD:
_painter->drawChord( _zoomHandler->zoomItX(pw), _zoomHandler->zoomItY(pw),
_zoomHandler->zoomItX(ow - 2 * pw),
_zoomHandler->zoomItY(oh - 2 * pw), p_angle, p_len );
break;
default: break;
}
}
void KPrPieObject::flip( bool horizontal )
{
KPr2DObject::flip( horizontal );
if ( ! horizontal )
{
p_angle = 360*16 - p_angle -p_len;
}
else
{
p_angle = 180*16 - p_angle - p_len;
}
// angle smaller 0
while ( p_angle < 0 ) {
p_angle += 360*16;
}
}
void KPrPieObject::setMinMax( double &min_x, double &min_y,
double &max_x, double &max_y, KoPoint point ) const
{
double tmp_x = point.x();
double tmp_y = point.y();
if ( tmp_x < min_x ) {
min_x = tmp_x;
}
else if ( tmp_x > max_x ) {
max_x = tmp_x;
}
if ( tmp_y < min_y ) {
min_y = tmp_y;
}
else if ( tmp_y > max_y ) {
max_y = tmp_y;
}
}
/*
* The calculation of the real size and origin for a pie object is a little more
* complicated. It took me quite a whlie to get it right.
* Here is how it works:
* 1. calculate the position of the end points
* 2. calculate the 4 maximal points, the points with max x or y position, of the
* hole ellipse
* 3. find minimal and maximal points
* 4. check if the maximal points lie on the arc
*
*/
void KPrPieObject::getRealSizeAndOrig( KoSize &size, KoPoint &realOrig ) const {
double radius1 = size.width() / 2.0;
double radius2 = size.height() / 2.0;
// the rotation angle
double angInRad = angle * M_PI / 180;
// 1. calulate position of end points
KoPointArray points(2);
setEndPoints( points );
// rotate point
for ( int i = 0; i < 2; i++ ) {
if ( angle != 0 ) {
double sinus = sin( angInRad );
double cosinus = cos( angInRad );
double tmp_x = points.point( i ).x();
double tmp_y = points.point( i ).y();
double x = tmp_x * cosinus + tmp_y * sinus;
double y = - tmp_x * sinus + tmp_y * cosinus;
points.setPoint( i, x, y );
}
}
KoPoint firstPoint( points.point(0) );
KoPoint secondPoint( points.point(1) );
// 2. calulate maximal points
KoPointArray maxPoints(4);
if ( angle == 0 ) {
maxPoints.setPoint( 0, 0, radius2 );
maxPoints.setPoint( 1, radius1, 0 );
maxPoints.setPoint( 2, 0, -radius2 );
maxPoints.setPoint( 3, -radius1, 0 );
}
else {
double sinus = sin( angInRad );
double cosinus = cos( angInRad );
double x = sqrt( pow( radius1 * cosinus , 2 ) + pow(radius2 * sinus, 2));
double y = ( pow( radius2, 2 ) - pow( radius1, 2) ) * sinus * cosinus / x;
maxPoints.setPoint( 0, x, y );
maxPoints.setPoint( 1, -x, -y );
y = sqrt( pow( radius1 * sinus , 2 ) + pow(radius2 * cosinus, 2));
x = ( pow( radius1, 2 ) - pow( radius2, 2) ) * sinus * cosinus / y;
maxPoints.setPoint( 2, x, y);
maxPoints.setPoint( 3, -x, -y );
}
// 3. find minimal and maximal points
double min_x = firstPoint.x();
double min_y = firstPoint.y();
double max_x = firstPoint.x();
double max_y = firstPoint.y();
if ( pieType == PT_PIE ) {
KoPoint zero(0,0);
setMinMax( min_x, min_y, max_x, max_y, zero );
}
setMinMax( min_x, min_y, max_x, max_y, secondPoint );
/* 4. check if maximal points lie on the arc.
* There are three posibilities how many sections have to
* been checked.
* 1. the arc is only once on one side of the x axis
* 2. the arc is on both sides of the x axis
* 3. the arc is twice on one one side of the x axis
*
* 1) 2) 3)
* y y y
* ex|xx xx|xs s |
* | x x | x | e
* | s x | x | x
* ----+---- x ----+---- x ----+---- x
* | x | x | x
* | x | x | x
* | e | xx|xx
*
*/
if ( firstPoint.y() >= 0 ) {
if ( secondPoint.y() >= 0 ) {
if ( firstPoint.x() > secondPoint.x() || p_len == 0 ) {
// 1 section
// f.x() <= x <= s.x() && y >= 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() >= 0 &&
(*it).x() <= firstPoint.x() && (*it).x() >= secondPoint.x() )
{
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
else {
// 3 sections
// x <= f.x() && y >= 0
// y < 0
// x >= s.x() && y >= 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() >= 0 ) {
if ( (*it).x() <= firstPoint.x() || (*it).x() >= secondPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
else {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
}
else {
// 2 sections
// x <= f.x() && y >= 0
// x <= s.x() && y < 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() >= 0 ) {
if ( (*it).x() <= firstPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
else {
if ( (*it).x() <= secondPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
}
}
else {
if ( secondPoint.y() >= 0 ) {
// 2 sections
// x >= f.x() && y < 0
// x >= s.x() && y >= 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() < 0 ) {
if ( (*it).x() >= firstPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
else {
if ( (*it).x() >= secondPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
}
else {
if ( firstPoint.x() < secondPoint.x() || p_len == 0 ) {
// 1 section
// f.x() <= x <= s.x() && y < 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() < 0 &&
(*it).x() >= firstPoint.x() && (*it).x() <= secondPoint.x() )
{
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
else {
// 3 sections
// x >= f.x() && y < 0
// y >= 0
// x <= s.x() && y < 0
KoPointArray::ConstIterator it( maxPoints.begin() );
for ( ; it != maxPoints.end(); ++it ) {
if ( (*it).y() < 0 ) {
if ( (*it).x() >= firstPoint.x() || (*it).x() <= secondPoint.x() ) {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
else {
setMinMax( min_x, min_y, max_x, max_y, *it );
}
}
}
}
}
double mid_x = size.width() / 2;
double mid_y = size.height() / 2;
size.setWidth( max_x - min_x );
size.setHeight( max_y - min_y );
realOrig.setX( realOrig.x() + mid_x + min_x );
realOrig.setY( realOrig.y() + mid_y - max_y );
}
void KPrPieObject::setEndPoints( KoPointArray &points ) const
{
int angles[] = { p_angle, ( p_angle + p_len ) % ( 16 * 360 ) };
double anglesInRad[] = { p_angle / 16.0 * M_PI / 180, ( angles[1] ) / 16.0 * M_PI / 180 };
double radius1 = ext.width() / 2.0;
double radius2 = ext.height() / 2.0;
double prop = radius2 / radius1;
for ( int i = 0; i < 2; i++ ) {
double x = 0;
double y = 0;
// be carefull
if ( angles[i] == 90 * 16 ) {
y = radius2;
}
else if ( angles[i] == 270 * 16 ) {
y = -radius2;
}
else {
// The real angle is not what was given. It is only ok if radius1 == radius2,
// otherwise it is arctan ( radius2 / radius1 tan ( angle ) )
double tanalpha = tan( anglesInRad[i] ) * prop;
x = sqrt( 1 / ( pow ( 1 / radius1, 2 ) + pow( tanalpha / radius2, 2 ) ) );
if ( angles[i] > 90 * 16 && angles[i] < 270 * 16 )
x = -x;
y = tanalpha * x;
}
points.setPoint( i, x, y );
}
}
KoSize KPrPieObject::getRealSize() const {
KoSize size( ext );
KoPoint realOrig( orig );
getRealSizeAndOrig( size, realOrig );
return size;
}
KoPoint KPrPieObject::getRealOrig() const {
KoSize size( ext );
KoPoint realOrig( orig );
getRealSizeAndOrig( size, realOrig );
return realOrig;
}