29#include <KoXmlWriter.h>
31#include <KoGenStyles.h>
33#include "vcomposite.h"
34#include "vcomposite_iface.h"
41#include "commands/vtransformcmd.h"
47VPath::VPath(
VObject* parent, VState state )
48 :
VObject( parent, state ), m_fillRule( winding )
50 m_paths.setAutoDelete(
true );
53 m_paths.append(
new VSubpath(
this ) );
59 m_drawCenterNode =
false;
62VPath::VPath(
const VPath& composite )
63 :
VObject( composite ), SVGPathParser()
65 m_paths.setAutoDelete(
true );
69 VSubpathListIterator itr( composite.m_paths );
70 for( itr.toFirst(); itr.current(); ++itr )
72 path = itr.current()->
clone();
74 m_paths.append( path );
78 setStroke( *composite.
stroke() );
80 if ( composite.
fill() )
81 setFill( *composite.
fill() );
83 m_drawCenterNode =
false;
84 m_fillRule = composite.m_fillRule;
85 m_matrix = composite.m_matrix;
95 m_dcop =
new VPathIface(
this );
117 VSubpathListIterator itr( m_paths );
122 for( itr.toFirst(); itr.current(); ++itr )
124 if( !itr.current()->isEmpty() )
127 painter->setRasterOp( TQt::XorROP );
128 painter->setPen( TQt::yellow );
129 painter->setBrush( TQt::NoBrush );
132 for( ; jtr.current(); ++jtr )
134 jtr.current()->draw( painter );
137 painter->strokePath();
145 painter->setFillRule( m_fillRule );
147 for( itr.toFirst(); itr.current(); ++itr )
149 if( !itr.current()->isEmpty() )
152 for( ; jtr.current(); ++jtr )
154 jtr.current()->draw( painter );
159 painter->setRasterOp( TQt::CopyROP );
160 painter->setPen( TQt::NoPen );
161 painter->setBrush( *
fill() );
165 painter->setPen( *
stroke() );
166 painter->setBrush( TQt::NoBrush );
167 painter->strokePath();
176 return m_paths.getLast()->currentPoint();
180VPath::moveTo(
const KoPoint& p )
183 if( !m_paths.getLast()->isEmpty() )
186 m_paths.append( path );
189 return m_paths.getLast()->moveTo( p );
193VPath::lineTo(
const KoPoint& p )
195 return m_paths.getLast()->lineTo( p );
200 const KoPoint& p1,
const KoPoint& p2,
const KoPoint& p3 )
202 return m_paths.getLast()->curveTo( p1, p2, p3 );
206VPath::curve1To(
const KoPoint& p2,
const KoPoint& p3 )
208 return m_paths.getLast()->curve1To( p2, p3 );
212VPath::curve2To(
const KoPoint& p1,
const KoPoint& p3 )
214 return m_paths.getLast()->curve2To( p1, p3 );
220 return m_paths.getLast()->arcTo( p1, p2, r );
226 m_paths.getLast()->close();
231 m_paths.append( path );
235VPath::isClosed()
const
237 return m_paths.getLast()->isEmpty() || m_paths.getLast()->isClosed();
243 VSubpathListIterator itr( composite.m_paths );
244 for( ; itr.current(); ++itr )
260 m_fillRule = fillMode();
271 VSubpathListIterator itr( m_paths );
273 for( itr.toFirst(); itr.current(); ++itr )
275 if( itr.current()->pointIsInside( p ) )
290 VSubpathListIterator itr( m_paths );
292 for( itr.toFirst(); itr.current(); ++itr )
294 if( itr.current()->intersects( segment ) )
303VPath::fillMode()
const
305 return ( m_paths.count() > 1 ) ? evenOdd : winding;
313 VSubpathListIterator itr( m_paths );
316 m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect();
318 for( ++itr; itr.current(); ++itr )
339 return new VPath( *
this );
347 TQDomElement me = element.ownerDocument().createElement(
"PATH" );
348 element.appendChild( me );
354 me.setAttribute(
"d", d );
359 if( !( m_fillRule == evenOdd ) )
360 me.setAttribute(
"fillRule", m_fillRule );
365VPath::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles,
int &index )
const
369 docWriter->startElement(
"draw:path" );
373 docWriter->addAttribute(
"svg:d", d );
380 docWriter->addAttribute(
"svg:viewBox", TQString(
"%1 %2 %3 %4" ).arg( x ).arg( y ).arg( w ).arg( h ) );
381 docWriter->addAttributePt(
"svg:x", x );
382 docWriter->addAttributePt(
"svg:y", y );
383 docWriter->addAttributePt(
"svg:width", w );
384 docWriter->addAttributePt(
"svg:height", h );
389 tmpMat.scale( 1, -1 );
390 tmpMat.translate( 0, -
document()->height() );
392 TQString transform = buildOasisTransform( tmpMat );
393 if( !transform.isEmpty() )
394 docWriter->addAttribute(
"draw:transform", transform );
396 docWriter->endElement();
401VPath::saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesobjectauto )
const
407 mat.translate( 0, -
document()->height() );
411 fill.transform( mat );
412 fill.saveOasis( mainStyles, stylesobjectauto );
414 if( !( m_fillRule == evenOdd ) )
415 stylesobjectauto.addProperty(
"svg:fill-rule",
"winding" );
420VPath::transformByViewbox(
const TQDomElement &element, TQString viewbox )
422 if( ! viewbox.isEmpty() )
425 TQStringList points = TQStringList::split(
' ', viewbox.replace(
',',
' ' ).simplifyWhiteSpace() );
427 double w = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg,
"width", TQString() ) );
428 double h = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg,
"height", TQString() ) );
429 double x = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg,
"x", TQString() ) );
430 double y = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg,
"y", TQString() ) );
433 mat.translate( x-KoUnit::parseValue( points[0] ), y-KoUnit::parseValue( points[1] ) );
434 mat.scale( w / KoUnit::parseValue( points[2] ) , h / KoUnit::parseValue( points[3] ) );
435 VTransformCmd cmd( 0L, mat );
436 cmd.visitVPath( *
this );
447 if( element.localName() ==
"path" )
449 TQString data = element.attributeNS( KoXmlNS::svg,
"d", TQString() );
450 if( data.length() > 0 )
455 m_fillRule = element.attributeNS( KoXmlNS::svg,
"fill-rule", TQString() ) ==
"winding" ? winding : evenOdd;
457 viewbox = element.attributeNS( KoXmlNS::svg,
"viewBox", TQString() );
459 else if( element.localName() ==
"custom-shape" )
461 TQDomNodeList list = element.childNodes();
462 for( uint i = 0; i < list.count(); ++i )
464 if( list.item( i ).isElement() )
466 TQDomElement e = list.item( i ).toElement();
467 if( e.namespaceURI() != KoXmlNS::draw )
470 if( e.localName() ==
"enhanced-geometry" )
472 TQString data = e.attributeNS( KoXmlNS::draw,
"enhanced-path", TQString() );
473 if( ! data.isEmpty() )
476 viewbox = e.attributeNS( KoXmlNS::svg,
"viewBox", TQString() );
482 transformByViewbox( element, viewbox );
484 TQString trafo = element.attributeNS( KoXmlNS::draw,
"transform", TQString() );
485 if( !trafo.isEmpty() )
486 transformOasis( trafo );
498 TQString data = element.attribute(
"d" );
499 if( data.length() > 0 )
503 m_fillRule = element.attribute(
"fillRule" ) == 0 ? evenOdd : winding;
504 TQDomNodeList list = element.childNodes();
505 for( uint i = 0; i < list.count(); ++i )
507 if( list.item( i ).isElement() )
509 TQDomElement child = list.item( i ).toElement();
511 if( child.tagName() ==
"PATH" )
525 TQString trafo = element.attribute(
"transform" );
526 if( !trafo.isEmpty() )
531VPath::loadSvgPath(
const TQString &d )
539VPath::saveSvgPath( TQString &d )
const
542 VSubpathListIterator itr( m_paths );
543 for( itr.toFirst(); itr.current(); ++itr )
545 if( !itr.current()->isEmpty() )
546 itr.current()->saveSvgPath( d );
553 moveTo( KoPoint( x1, y1 ) );
557VPath::svgLineTo(
double x1,
double y1,
bool )
559 lineTo( KoPoint( x1, y1 ) );
563VPath::svgCurveToCubic(
double x1,
double y1,
double x2,
double y2,
double x,
double y,
bool )
565 curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x, y ) );
581VPath::transform(
const TQString &transform )
583 VTransformCmd cmd( 0L, parseTransform( transform ) );
584 cmd.visitVPath( *
this );
588VPath::transformOasis(
const TQString &transform )
590 VTransformCmd cmd( 0L, parseOasisTransform( transform ) );
591 cmd.visitVPath( *
this );
595VPath::parseTransform(
const TQString &transform )
600 TQStringList subtransforms = TQStringList::split(
')', transform);
601 TQStringList::ConstIterator it = subtransforms.begin();
602 TQStringList::ConstIterator end = subtransforms.end();
603 for(; it != end; ++it)
605 TQStringList subtransform = TQStringList::split(
'(', (*it));
607 subtransform[0] = subtransform[0].stripWhiteSpace().lower();
608 subtransform[1] = subtransform[1].simplifyWhiteSpace();
609 TQRegExp reg(
"[,( ]");
610 TQStringList params = TQStringList::split(reg, subtransform[1]);
612 if(subtransform[0].startsWith(
";") || subtransform[0].startsWith(
","))
613 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
615 if(subtransform[0] ==
"rotate")
617 if(params.count() == 3)
619 double x = params[1].toDouble();
620 double y = params[2].toDouble();
622 result.translate(x, y);
623 result.rotate(params[0].toDouble());
624 result.translate(-x, -y);
627 result.rotate(params[0].toDouble());
629 else if(subtransform[0] ==
"translate")
631 if(params.count() == 2)
632 result.translate(params[0].toDouble(), params[1].toDouble());
634 result.translate(params[0].toDouble() , 0);
636 else if(subtransform[0] ==
"scale")
638 if(params.count() == 2)
639 result.scale(params[0].toDouble(), params[1].toDouble());
641 result.scale(params[0].toDouble(), params[0].toDouble());
643 else if(subtransform[0] ==
"skewx")
644 result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F);
645 else if(subtransform[0] ==
"skewy")
646 result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F);
647 else if(subtransform[0] ==
"skewy")
648 result.shear(0.0F, tan(params[0].toDouble() * VGlobal::pi_180));
649 else if(subtransform[0] ==
"matrix")
651 if(params.count() >= 6)
652 result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), params[4].toDouble(), params[5].toDouble());
660VPath::parseOasisTransform(
const TQString &transform )
665 TQStringList subtransforms = TQStringList::split(
')', transform);
666 TQStringList::ConstIterator it = subtransforms.begin();
667 TQStringList::ConstIterator end = subtransforms.end();
668 for(; it != end; ++it)
670 TQStringList subtransform = TQStringList::split(
'(', (*it));
672 subtransform[0] = subtransform[0].stripWhiteSpace().lower();
673 subtransform[1] = subtransform[1].simplifyWhiteSpace();
674 TQRegExp reg(
"[,( ]");
675 TQStringList params = TQStringList::split(reg, subtransform[1]);
677 if(subtransform[0].startsWith(
";") || subtransform[0].startsWith(
","))
678 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
680 if(subtransform[0] ==
"rotate")
683 if(params.count() == 3)
685 double x = KoUnit::parseValue( params[1] );
686 double y = KoUnit::parseValue( params[2] );
688 result.translate(x, y);
690 result.rotate( params[0].toDouble()*VGlobal::one_pi_180 );
691 result.translate(-x, -y);
696 result.rotate( params[0].toDouble()*VGlobal::one_pi_180 );
699 else if(subtransform[0] ==
"translate")
701 if(params.count() == 2)
703 double x = KoUnit::parseValue( params[0] );
704 double y = KoUnit::parseValue( params[1] );
705 result.translate(x, y);
708 result.translate( KoUnit::parseValue( params[0] ) , 0);
710 else if(subtransform[0] ==
"scale")
712 if(params.count() == 2)
713 result.scale(params[0].toDouble(), params[1].toDouble());
715 result.scale(params[0].toDouble(), params[0].toDouble());
717 else if(subtransform[0] ==
"skewx")
718 result.shear(tan(params[0].toDouble()), 0.0F);
719 else if(subtransform[0] ==
"skewy")
720 result.shear(tan(params[0].toDouble()), 0.0F);
721 else if(subtransform[0] ==
"skewy")
722 result.shear(0.0F, tan(params[0].toDouble()));
723 else if(subtransform[0] ==
"matrix")
725 if(params.count() >= 6)
726 result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), KoUnit::parseValue( params[4] ), KoUnit::parseValue( params[5] ) );
734VPath::buildSvgTransform()
const
736 return buildSvgTransform( m_matrix );
740VPath::buildSvgTransform(
const TQWMatrix &mat )
const
743 if( !mat.isIdentity() )
745 transform = TQString(
"matrix(%1, %2, %3, %4, %5, %6)" ).arg( mat.m11() )
756VPath::buildOasisTransform()
const
758 return buildSvgTransform( m_matrix );
762VPath::buildOasisTransform(
const TQWMatrix &mat )
const
765 if( !mat.isIdentity() )
767 transform = TQString(
"matrix(%1, %2, %3, %4, %5pt, %6pt)" ).arg( mat.m11() )
Manages the fill of shapes.
The base class for all karbon objects.
bool m_boundingBoxIsInvalid
the flag stating if the bounding box is valid
DCOPObject * m_dcop
the object's DCOP object
virtual void setState(const VState state)
Sets the state to a specified new state.
virtual void saveOasis(KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index) const
Save this object's state to OpenDocument.
@ hidden_locked
hidden and locked (r/o)
@ deleted
deleted, nearly dead
@ normal
visible, not active
@ edit
visible, active and is currently manipulated by a tool
void setParent(VObject *parent)
Sets a new parent object.
VFill * m_fill
the object's fill
virtual VFill * fill() const
Gets the object's actual fill.
KoRect m_boundingBox
the object's bounding box
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
virtual VStroke * stroke() const
Gets the object's actual stroke.
virtual void save(TQDomElement &element) const
Save this object's state to xml.
VDocument * document() const
Return document the object belongs to.
virtual bool loadOasis(const TQDomElement &element, KoOasisLoadingContext &context)
Load this object's state from OpenDocument and initialize this object accordingly.
VState state() const
Get the state the object is in.
A composite path consists of one or many subpaths.
virtual bool loadOasis(const TQDomElement &element, KoOasisLoadingContext &context)
Load this object's state from OpenDocument and initialize this object accordingly.
virtual void saveOasis(KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index) const
Save this object's state to OpenDocument.
const KoPoint & currentPoint() const
Returns the knot of the last segment of the last subpath.
virtual void draw(VPainter *painter, const KoRect *rect=0L) const
Draw the object to a painting device.
void close()
Closes the current subpath.
virtual void accept(VVisitor &visitor)
Accept a VVisitor.
void combine(const VPath &path)
Combines two composite paths.
bool arcTo(const KoPoint &p1, const KoPoint &p2, double r)
A convenience function to aproximate a circular arc with a bezier curve.
void combinePath(const VSubpath &path)
Adds a path to the composite path.
bool pointIsInside(const KoPoint &p) const
Returns true if point p is located inside the composite.
bool intersects(const VSegment &segment) const
Returns true if the segment intersects this composite.
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
virtual void save(TQDomElement &element) const
Save this object's state to xml.
virtual const KoRect & boundingBox() const
Calculates the tightest bounding box around the object.
virtual VPath * clone() const
Create an exact copy of this object.
virtual void svgMoveTo(double x1, double y1, bool abs=true)
For svg path data parsing.
virtual DCOPObject * dcopObject()
Returns pointer to internal DCOP object.
A class representing lines and beziers.
VSegment * clone() const
Returns a pointer to a copy of this segment.
KoRect boundingBox() const
Calculates the bounding box.
Manages stroke properties.
VSubpathIterator provides an iterator class for highlevel path access.
VSubpath provides a sophisticated list of VSegment.
virtual VSubpath * clone() const
Create an exact copy of this object.
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
The abstract visitor class.
virtual void visitVPath(VPath &composite)
Visit method for a VPath.