#i105939# Adds special box clipping support to basegfx

This commit is contained in:
thb
2009-10-16 00:53:07 +02:00
parent 44dfc8de1a
commit 8454c5ef50
15 changed files with 2454 additions and 637 deletions

View File

@@ -83,7 +83,7 @@ namespace basegfx
sal_uInt32 count() const;
/// Coordinate interface
basegfx::B2DPoint getB2DPoint(sal_uInt32 nIndex) const;
const basegfx::B2DPoint& getB2DPoint(sal_uInt32 nIndex) const;
void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue);
/// Coordinate insert/append
@@ -201,7 +201,7 @@ namespace basegfx
@return
The outer range of the bezier curve/polygon
*/
B2DRange getB2DRange() const;
const B2DRange& getB2DRange() const;
/** insert other 2D polygons
@@ -261,6 +261,12 @@ namespace basegfx
/// apply transformation given in matrix form
void transform(const basegfx::B2DHomMatrix& rMatrix);
// point iterators
const B2DPoint* begin() const;
const B2DPoint* end() const;
B2DPoint* begin();
B2DPoint* end();
};
} // end of namespace basegfx

View File

@@ -128,6 +128,12 @@ namespace basegfx
// apply transformation given in matrix form to the polygon
void transform(const basegfx::B2DHomMatrix& rMatrix);
// polygon iterators
const B2DPolygon* begin() const;
const B2DPolygon* end() const;
B2DPolygon* begin();
B2DPolygon* end();
};
} // end of namespace basegfx

View File

@@ -1,117 +0,0 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.hxx,v $
* $Revision: 1.6 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#ifndef _BGFX_RANGE_B2DMULTIRANGE_HXX
#define _BGFX_RANGE_B2DMULTIRANGE_HXX
#include <o3tl/cow_wrapper.hxx>
#include <memory>
namespace basegfx
{
class B2DTuple;
class B2DRange;
class B2DPolyPolygon;
class ImplB2DMultiRange;
/** Multiple ranges in one object.
This class combines multiple ranges in one object, providing a
total, enclosing range for it.
You can use this class e.g. when updating views containing
rectangular objects. Add each modified object to a
B2DMultiRange, then test each viewable object against
intersection with the multi range.
*/
class B2DMultiRange
{
public:
B2DMultiRange();
~B2DMultiRange();
/** Create a multi range with exactly one containing range
*/
explicit B2DMultiRange( const B2DRange& rRange );
B2DMultiRange( const B2DMultiRange& );
B2DMultiRange& operator=( const B2DMultiRange& );
/** Check whether range is empty.
@return true, if this object either contains no ranges at
all, or all contained ranges are empty.
*/
bool isEmpty() const;
/** Reset to empty.
After this call, the object will not contain any ranges,
and isEmpty() will return true.
*/
void reset();
/** Test whether given tuple is inside one or more of the
included ranges.
*/
bool isInside( const B2DTuple& rTuple ) const;
/** Test whether given range is inside one or more of the
included ranges.
*/
bool isInside( const B2DRange& rRange ) const;
/** Test whether given range overlaps one or more of the
included ranges.
*/
bool overlaps( const B2DRange& rRange ) const;
/** Add given range to the number of contained ranges.
*/
void addRange( const B2DRange& rRange );
/** Get overall bound rect for all included ranges.
*/
B2DRange getBounds() const;
/** Request poly-polygon representing the added ranges.
This method creates a poly-polygon, consisting exactly out
of the contained ranges.
*/
B2DPolyPolygon getPolyPolygon() const;
private:
o3tl::cow_wrapper< ImplB2DMultiRange > mpImpl;
};
}
#endif /* _BGFX_RANGE_B2DMULTIRANGE_HXX */

View File

@@ -0,0 +1,139 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.hxx,v $
* $Revision: 1.6 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#ifndef _BGFX_RANGE_B2DPOLYRANGE_HXX
#define _BGFX_RANGE_B2DPOLYRANGE_HXX
#include <o3tl/cow_wrapper.hxx>
#include <boost/tuple/tuple.hpp>
#include <basegfx/vector/b2enums.hxx>
namespace basegfx
{
class B2DTuple;
class B2DRange;
class B2DPolyPolygon;
class ImplB2DPolyRange;
/** Multiple ranges in one object.
This class combines multiple ranges in one object, providing a
total, enclosing range for it.
You can use this class e.g. when updating views containing
rectangular objects. Add each modified object to a
B2DMultiRange, then test each viewable object against
intersection with the multi range.
Similar in spirit to the poly-polygon vs. polygon relationship.
Note that comparable to polygons, a poly-range can also
contain 'holes' - this is encoded via polygon orientation at
the poly-polygon, and via explicit flags for the poly-range.
*/
class B2DPolyRange
{
public:
typedef boost::tuple<B2DRange,B2VectorOrientation> ElementType ;
B2DPolyRange();
~B2DPolyRange();
/** Create a multi range with exactly one containing range
*/
explicit B2DPolyRange( const ElementType& rElement );
B2DPolyRange( const B2DRange& rRange, B2VectorOrientation eOrient );
B2DPolyRange( const B2DPolyRange& );
B2DPolyRange& operator=( const B2DPolyRange& );
/// unshare this poly-range with all internally shared instances
void makeUnique();
bool operator==(const B2DPolyRange&) const;
bool operator!=(const B2DPolyRange&) const;
/// Number of included ranges
sal_uInt32 count() const;
ElementType getElement(sal_uInt32 nIndex) const;
void setElement(sal_uInt32 nIndex, const ElementType& rElement );
void setElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient );
// insert/append a single range
void insertElement(sal_uInt32 nIndex, const ElementType& rElement, sal_uInt32 nCount = 1);
void insertElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount = 1);
void appendElement(const ElementType& rElement, sal_uInt32 nCount = 1);
void appendElement(const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount = 1);
// insert/append multiple ranges
void insertPolyRange(sal_uInt32 nIndex, const B2DPolyRange&);
void appendPolyRange(const B2DPolyRange&);
void remove(sal_uInt32 nIndex, sal_uInt32 nCount = 1);
void clear();
// flip range orientations - converts holes to solids, and vice versa
void flip();
/** Get overall range
@return
The union range of all contained ranges
*/
B2DRange getBounds() const;
/** Test whether given tuple is inside one or more of the
included ranges. Does *not* use overall range, but checks
individually.
*/
bool isInside( const B2DTuple& rTuple ) const;
/** Test whether given range is inside one or more of the
included ranges. Does *not* use overall range, but checks
individually.
*/
bool isInside( const B2DRange& rRange ) const;
/** Test whether given range overlaps one or more of the
included ranges. Does *not* use overall range, but checks
individually.
*/
bool overlaps( const B2DRange& rRange ) const;
/** Request a poly-polygon with solved cross-overs
*/
B2DPolyPolygon solveCrossovers() const;
private:
o3tl::cow_wrapper< ImplB2DPolyRange > mpImpl;
};
}
#endif /* _BGFX_RANGE_B2DPOLYRANGE_HXX */

View File

@@ -0,0 +1,53 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.hxx,v $
* $Revision: 1.6 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#ifndef _BGFX_RANGE_B2DRANGECLIPPER_HXX
#define _BGFX_RANGE_B2DRANGECLIPPER_HXX
#include <basegfx/range/b2dpolyrange.hxx>
#include <vector>
namespace basegfx
{
namespace tools
{
/** Extract poly-polygon w/o self-intersections from poly-range
Similar to the solveCrossovers(const B2DPolyPolygon&)
method, this one calculates a self-intersection-free
poly-polygon with the same topology, and encoding
inside/outsidedness via polygon orientation and layering.
*/
B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges,
const std::vector<B2VectorOrientation>& rOrientations);
}
}
#endif /* _BGFX_RANGE_B2DRANGECLIPPER_HXX */

344
basegfx/qa/mkpolygons.pl Normal file
View File

@@ -0,0 +1,344 @@
:
eval 'exec perl -wS $0 ${1+"$@"}'
if 0;
#
# 2009 Copyright Novell, Inc. & Sun Microsystems, Inc.
#
# OpenOffice.org is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# only, as published by the Free Software Foundation.
#
use IO::File;
use Cwd;
use File::Spec;
use File::Spec::Functions;
use File::Temp;
use File::Path;
$TempDir = "";
# all the XML package generation is a blatant rip from AF's
# write-calc-doc.pl
###############################################################################
# Open a file with the given name.
# First it is checked if the temporary directory, in which all files for
# the document are gathered, is already present and create it if it is not.
# Then create the path to the file inside the temporary directory.
# Finally open the file and return a file handle to it.
#
sub open_file
{
my $filename = pop @_;
# Create base directory of temporary directory tree if not alreay
# present.
if ($TempDir eq "")
{
$TempDir = File::Temp::tempdir (CLEANUP => 1);
}
# Create the path to the file.
my $fullname = File::Spec->catfile ($TempDir, $filename);
my ($volume,$directories,$file) = File::Spec->splitpath ($fullname);
mkpath (File::Spec->catpath ($volume,$directories,""));
# Open the file and return a file handle to it.
return new IO::File ($fullname, "w");
}
###############################################################################
# Zip the files in the directory tree into the given file.
#
sub zip_dirtree
{
my $filename = pop @_;
my $cwd = getcwd;
my $zip_name = $filename;
# We are about to change the directory.
# Therefore create an absolute pathname for the zip archive.
# First transfer the drive from $cwd to $zip_name. This is a
# workaround for a bug in file_name_is_absolute which thinks
# the the path \bla is an absolute path under DOS.
my ($volume,$directories,$file) = File::Spec->splitpath ($zip_name);
my ($volume_cwd,$directories_cwd,$file_cwd) = File::Spec->splitpath ($cwd);
$volume = $volume_cwd if ($volume eq "");
$zip_name = File::Spec->catpath ($volume,$directories,$file);
# Add the current working directory to a relative path.
if ( ! file_name_is_absolute ($zip_name))
{
$zip_name = File::Spec->catfile ($cwd, $zip_name);
# Try everything to clean up the name.
$zip_name = File::Spec->rel2abs ($filename);
$zip_name = File::Spec->canonpath ($zip_name);
# Remove .. directories from the middle of the path.
while ($zip_name =~ /\/[^\/][^\.\/][^\/]*\/\.\.\//)
{
$zip_name = $` . "/" . $';
}
}
# Just in case the zip program gets confused by an existing file with the
# same name as the one to be written that file is removed first.
if ( -e $filename)
{
if (unlink ($filename) == 0)
{
print "Existing file $filename could not be deleted.\n";
print "Please close the application that uses it, then try again.\n";
return;
}
}
# Finally create the zip file. First change into the temporary directory
# so that the resulting zip file contains only paths relative to it.
print "zipping [$ZipCmd $ZipFlags $zip_name *]\n";
chdir ($TempDir);
system ("$ZipCmd $ZipFlags $zip_name *");
chdir ($cwd);
}
sub writeHeader
{
print $OUT qq~<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" office:version="1.0">
<office:scripts/>
<office:automatic-styles>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties presentation:background-visible="true" presentation:background-objects-visible="true" presentation:display-footer="true" presentation:display-page-number="false" presentation:display-date-time="true"/>
</style:style>
<style:style style:name="gr1" style:family="graphic" style:parent-style-name="standard">
<style:graphic-properties draw:textarea-horizontal-align="center" draw:fill="none" draw:stroke="none" draw:textarea-vertical-align="middle"/>
</style:style>
<style:style style:name="gr2" style:family="graphic" style:parent-style-name="standard">
<style:graphic-properties draw:textarea-horizontal-align="center" draw:textarea-vertical-align="middle"/>
</style:style>
<style:style style:name="pr1" style:family="presentation" style:parent-style-name="Default-title">
<style:graphic-properties draw:fill-color="#ffffff" draw:auto-grow-height="true" fo:min-height="3.508cm"/>
</style:style>
<style:style style:name="pr2" style:family="presentation" style:parent-style-name="Default-notes">
<style:graphic-properties draw:fill-color="#ffffff" draw:auto-grow-height="true" fo:min-height="13.367cm"/>
</style:style>
<style:style style:name="P1" style:family="paragraph">
<style:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:text-indent="0cm"/>
</style:style>
<style:style style:name="P2" style:family="paragraph">
<style:paragraph-properties fo:margin-left="0.6cm" fo:margin-right="0cm" fo:text-indent="-0.6cm"/>
</style:style>
<text:list-style style:name="L1">
<text:list-level-style-bullet text:level="1" text:bullet-char="●">
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="2" text:bullet-char="●">
<style:list-level-properties text:space-before="0.6cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="3" text:bullet-char="●">
<style:list-level-properties text:space-before="1.2cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="4" text:bullet-char="●">
<style:list-level-properties text:space-before="1.8cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="5" text:bullet-char="●">
<style:list-level-properties text:space-before="2.4cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="6" text:bullet-char="●">
<style:list-level-properties text:space-before="3cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="7" text:bullet-char="●">
<style:list-level-properties text:space-before="3.6cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="8" text:bullet-char="●">
<style:list-level-properties text:space-before="4.2cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
<text:list-level-style-bullet text:level="9" text:bullet-char="●">
<style:list-level-properties text:space-before="4.8cm" text:min-label-width="0.6cm"/>
<style:text-properties fo:font-family="StarSymbol" style:use-window-font-color="true" fo:font-size="45%"/>
</text:list-level-style-bullet>
</text:list-style>
</office:automatic-styles>
<office:body>
<office:presentation>
~;
}
sub writeSlideHeader
{
my $titleText = pop @_;
my $slideNum = pop @_;
print $OUT " <draw:page draw:name=\"page1\" draw:style-name=\"dp1\" draw:master-page-name=\"Default\">\n";
print $OUT " <office:forms form:automatic-focus=\"false\" form:apply-design-mode=\"false\"/>\n";
print $OUT " <draw:rect draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:id=\"id$slideNum\" draw:layer=\"layout\" svg:width=\"17.5cm\" svg:height=\"6cm\" svg:x=\"5cm\" svg:y=\"4cm\">\n";
print $OUT " <text:p text:style-name=\"P2\">Slide: $slideNum</text:p>\n";
print $OUT " <text:p text:style-name=\"P2\">Path: $titleText</text:p>\n";
print $OUT " </draw:rect>\n";
}
sub writeSlideFooter
{
print $OUT " <presentation:notes draw:style-name=\"dp1\">\n";
print $OUT " <draw:page-thumbnail draw:style-name=\"gr1\" draw:layer=\"layout\" svg:width=\"14.851cm\" svg:height=\"11.138cm\" svg:x=\"3.068cm\" svg:y=\"2.257cm\" draw:page-number=\"1\" presentation:class=\"page\"/>\n";
print $OUT " <draw:frame presentation:style-name=\"pr3\" draw:layer=\"layout\" svg:width=\"16.79cm\" svg:height=\"13.116cm\" svg:x=\"2.098cm\" svg:y=\"14.109cm\" presentation:class=\"notes\" presentation:placeholder=\"true\">\n";
print $OUT " <draw:text-box/>\n";
print $OUT " </draw:frame>\n";
print $OUT " </presentation:notes>\n";
print $OUT " </draw:page>\n";
}
sub writeFooter
{
print $OUT qq~ <presentation:settings presentation:full-screen="false"/>
</office:presentation>
</office:body>
</office:document-content>
~;
}
sub writePath
{
my $pathAry = pop @_;
my $path = $pathAry->[1];
my $viewBox = $pathAry->[0];
print $OUT " <draw:path draw:style-name=\"gr2\" draw:text-style-name=\"P1\" draw:layer=\"layout\" svg:width=\"10cm\" svg:height=\"10cm\" svg:x=\"5cm\" svg:y=\"5cm\" svg:viewBox=\"";
print $OUT $viewBox;
print $OUT "\" svg:d=\"";
print $OUT $path;
print $OUT "\">\n";
print $OUT " <text:p/>\n";
print $OUT " </draw:path>\n";
}
sub writeManifest
{
my $outFile = open_file("META-INF/manifest.xml");
print $outFile qq~<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.presentation" manifest:full-path="/"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
</manifest:manifest>
~;
$outFile->close;
}
###############################################################################
# Print usage information.
#
sub usage ()
{
print <<END_OF_USAGE;
usage: $0 <option>* [<SvgD-values>]
output-file-name defaults to polygons.odp.
-h Print this usage information.
-o output-file-name
END_OF_USAGE
}
###############################################################################
# Process the command line.
#
sub process_command_line
{
foreach (@ARGV)
{
if (/^-h/)
{
usage;
exit 0;
}
}
$global_output_name = "polygons.odp";
my $j = 0, $noMoreOptions = 0;
for (my $i=0; $i<$#ARGV; $i++)
{
if ( !$noMoreOptions and $ARGV[$i] eq "-o")
{
$i++;
$global_output_name = $ARGV[$i];
}
elsif ( !$noMoreOptions and $ARGV[$i] eq "--")
{
$noMoreOptions = 1;
}
elsif ( !$noMoreOptions and $ARGV[$i] =~ /^-/)
{
print "Unknown option $ARGV[$i]\n";
usage;
exit 1;
}
else
{
push(@paths, [$ARGV[$i],$ARGV[$i+1]]);
$i++;
}
}
print "output to $global_output_name\n";
}
###############################################################################
# Main
###############################################################################
$ZipCmd = $ENV{LOG_FILE_ZIP_CMD};
$ZipFlags = $ENV{LOG_FILE_ZIP_FLAGS};
# Provide default values for the zip command and it's flags.
if ( ! defined $ZipCmd)
{
$ZipCmd = "zip" unless defined $ZipCmd;
$ZipFlags = "-r -q" unless defined $ZipFlags;
}
process_command_line();
writeManifest();
$OUT = open_file( "content.xml" );
writeHeader();
$pathNum=0;
foreach $path (@paths)
{
writeSlideHeader($pathNum, $path->[1]);
writePath($path);
writeSlideFooter();
$pathNum++;
}
writeFooter();
$OUT->close;
zip_dirtree ($global_output_name);

View File

@@ -44,38 +44,24 @@
//////////////////////////////////////////////////////////////////////////////
class CoordinateData2D
struct CoordinateData2D : public basegfx::B2DPoint
{
basegfx::B2DPoint maPoint;
public:
CoordinateData2D()
: maPoint()
{}
CoordinateData2D() {}
explicit CoordinateData2D(const basegfx::B2DPoint& rData)
: maPoint(rData)
: B2DPoint(rData)
{}
const basegfx::B2DPoint& getCoordinate() const
CoordinateData2D& operator=(const basegfx::B2DPoint& rData)
{
return maPoint;
}
void setCoordinate(const basegfx::B2DPoint& rValue)
{
if(rValue != maPoint)
maPoint = rValue;
}
bool operator==(const CoordinateData2D& rData ) const
{
return (maPoint == rData.getCoordinate());
B2DPoint::operator=(rData);
return *this;
}
void transform(const basegfx::B2DHomMatrix& rMatrix)
{
maPoint *= rMatrix;
*this *= rMatrix;
}
};
@@ -115,12 +101,12 @@ public:
const basegfx::B2DPoint& getCoordinate(sal_uInt32 nIndex) const
{
return maVector[nIndex].getCoordinate();
return maVector[nIndex];
}
void setCoordinate(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
{
maVector[nIndex].setCoordinate(rValue);
maVector[nIndex] = rValue;
}
void insert(sal_uInt32 nIndex, const CoordinateData2D& rValue, sal_uInt32 nCount)
@@ -221,6 +207,26 @@ public:
aStart->transform(rMatrix);
}
}
const basegfx::B2DPoint* begin() const
{
return &maVector.front();
}
const basegfx::B2DPoint* end() const
{
return &maVector[maVector.size()];
}
basegfx::B2DPoint* begin()
{
return &maVector.front();
}
basegfx::B2DPoint* end()
{
return &maVector[maVector.size()];
}
};
//////////////////////////////////////////////////////////////////////////////
@@ -1113,6 +1119,28 @@ public:
maPoints.transform(rMatrix);
}
}
const basegfx::B2DPoint* begin() const
{
return maPoints.begin();
}
const basegfx::B2DPoint* end() const
{
return maPoints.end();
}
basegfx::B2DPoint* begin()
{
mpBufferedData.reset();
return maPoints.begin();
}
basegfx::B2DPoint* end()
{
mpBufferedData.reset();
return maPoints.end();
}
};
//////////////////////////////////////////////////////////////////////////////
@@ -1173,7 +1201,7 @@ namespace basegfx
return mpPolygon->count();
}
B2DPoint B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
const B2DPoint& B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
@@ -1432,7 +1460,7 @@ namespace basegfx
return mpPolygon->getDefaultAdaptiveSubdivision(*this);
}
B2DRange B2DPolygon::getB2DRange() const
const B2DRange& B2DPolygon::getB2DRange() const
{
return mpPolygon->getB2DRange(*this);
}
@@ -1540,6 +1568,26 @@ namespace basegfx
mpPolygon->transform(rMatrix);
}
}
const B2DPoint* B2DPolygon::begin() const
{
return mpPolygon->begin();
}
const B2DPoint* B2DPolygon::end() const
{
return mpPolygon->end();
}
B2DPoint* B2DPolygon::begin()
{
return mpPolygon->begin();
}
B2DPoint* B2DPolygon::end()
{
return mpPolygon->end();
}
} // end of namespace basegfx
//////////////////////////////////////////////////////////////////////////////

View File

@@ -166,6 +166,26 @@ public:
maPolygons.end(),
std::mem_fun_ref( &basegfx::B2DPolygon::makeUnique ));
}
const basegfx::B2DPolygon* begin() const
{
return &maPolygons.front();
}
const basegfx::B2DPolygon* end() const
{
return &maPolygons[maPolygons.size()];
}
basegfx::B2DPolygon* begin()
{
return &maPolygons.front();
}
basegfx::B2DPolygon* end()
{
return &maPolygons[maPolygons.size()];
}
};
//////////////////////////////////////////////////////////////////////////////
@@ -378,6 +398,26 @@ namespace basegfx
mpPolyPolygon->transform(rMatrix);
}
}
const B2DPolygon* B2DPolyPolygon::begin() const
{
return mpPolyPolygon->begin();
}
const B2DPolygon* B2DPolyPolygon::end() const
{
return mpPolyPolygon->end();
}
B2DPolygon* B2DPolyPolygon::begin()
{
return mpPolyPolygon->begin();
}
B2DPolygon* B2DPolyPolygon::end()
{
return mpPolyPolygon->end();
}
} // end of namespace basegfx
// eof

View File

@@ -1,282 +0,0 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.cxx,v $
* $Revision: 1.8 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
#include <basegfx/range/b2drange.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/range/b2dmultirange.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>
#include <algorithm>
#include <vector>
namespace basegfx
{
class ImplB2DMultiRange
{
public:
ImplB2DMultiRange() :
maBounds(),
maRanges()
{
}
explicit ImplB2DMultiRange( const B2DRange& rRange ) :
maBounds(),
maRanges( 1, rRange )
{
}
bool isEmpty() const
{
// no ranges at all, or all ranges empty
return maRanges.empty() ||
::std::count_if( maRanges.begin(),
maRanges.end(),
::boost::mem_fn( &B2DRange::isEmpty ) )
== static_cast<VectorOfRanges::difference_type>(maRanges.size());
}
void reset()
{
// swap in empty vector
VectorOfRanges aTmp;
maRanges.swap( aTmp );
maBounds.reset();
}
template< typename ValueType > bool isInside( const ValueType& rValue ) const
{
if( !maBounds.isInside( rValue ) )
return false;
// cannot use ::boost::bind here, since isInside is overloaded.
// It is currently not possible to resolve the overload
// by considering one of the other template arguments.
VectorOfRanges::const_iterator aCurr( maRanges.begin() );
const VectorOfRanges::const_iterator aEnd ( maRanges.end() );
while( aCurr != aEnd )
if( aCurr->isInside( rValue ) )
return true;
return false;
}
bool overlaps( const B2DRange& rRange ) const
{
if( !maBounds.overlaps( rRange ) )
return false;
const VectorOfRanges::const_iterator aEnd( maRanges.end() );
return ::std::find_if( maRanges.begin(),
aEnd,
::boost::bind<bool>( ::boost::mem_fn( &B2DRange::overlaps ),
_1,
rRange ) ) != aEnd;
}
void addRange( const B2DRange& rRange )
{
maRanges.push_back( rRange );
maBounds.expand( rRange );
}
B2DRange getBounds() const
{
return maBounds;
}
B2DPolyPolygon getPolyPolygon() const
{
B2DPolyPolygon aRes;
// Make range vector unique ( have to avoid duplicate
// rectangles. The polygon clipper will return an empty
// result in this case).
VectorOfRanges aUniqueRanges;
aUniqueRanges.reserve( maRanges.size() );
VectorOfRanges::const_iterator aCurr( maRanges.begin() );
const VectorOfRanges::const_iterator aEnd ( maRanges.end() );
while( aCurr != aEnd )
{
// TODO(F3): It's plain wasted resources to apply a
// general clipping algorithm to the problem at
// hand. Go for a dedicated, scan-line-based approach.
VectorOfRanges::const_iterator aCurrScan( aCurr+1 );
VectorOfRanges::const_iterator aFound( aEnd );
while( aCurrScan != aEnd )
{
if( aCurrScan->equal( *aCurr ) ||
aCurrScan->isInside( *aCurr ) )
{
// current probe is equal to aCurr, or
// completely contains aCurr. Thus, stop
// searching, because aCurr is definitely not
// a member of the unique rect list
aFound = aCurrScan;
break;
}
++aCurrScan;
}
if( aFound == aEnd )
{
// check whether aCurr is fully contained in one
// of the already added rects. If yes, we can skip
// it.
bool bUnique( true );
VectorOfRanges::const_iterator aCurrUnique( aUniqueRanges.begin() );
VectorOfRanges::const_iterator aEndUnique ( aUniqueRanges.end() );
while( aCurrUnique != aEndUnique )
{
if( aCurrUnique->isInside( *aCurr ) )
{
// fully contained, no need to add
bUnique = false;
break;
}
++aCurrUnique;
}
if( bUnique )
aUniqueRanges.push_back( *aCurr );
}
++aCurr;
}
VectorOfRanges::const_iterator aCurrUnique( aUniqueRanges.begin() );
const VectorOfRanges::const_iterator aEndUnique ( aUniqueRanges.end() );
while( aCurrUnique != aEndUnique )
{
// simply merge all unique parts (OR)
aRes.append( tools::createPolygonFromRect( *aCurrUnique++ ) );
}
// remove redundant intersections. Note: since all added
// rectangles are positively oriented, this method cannot
// generate any holes.
aRes = basegfx::tools::solveCrossovers(aRes);
aRes = basegfx::tools::stripNeutralPolygons(aRes);
aRes = basegfx::tools::stripDispensablePolygons(aRes, false);
return aRes;
}
private:
typedef ::std::vector< B2DRange > VectorOfRanges;
B2DRange maBounds;
VectorOfRanges maRanges;
};
// ====================================================================
B2DMultiRange::B2DMultiRange() :
mpImpl()
{
}
B2DMultiRange::B2DMultiRange( const B2DRange& rRange ) :
mpImpl( ImplB2DMultiRange( rRange ) )
{
}
B2DMultiRange::~B2DMultiRange()
{
// otherwise, ImplB2DMultiRange would be an incomplete type
}
B2DMultiRange::B2DMultiRange( const B2DMultiRange& rSrc ) :
mpImpl( rSrc.mpImpl )
{
}
B2DMultiRange& B2DMultiRange::operator=( const B2DMultiRange& rSrc )
{
mpImpl = rSrc.mpImpl;
return *this;
}
bool B2DMultiRange::isEmpty() const
{
return mpImpl->isEmpty();
}
void B2DMultiRange::reset()
{
mpImpl->reset();
}
bool B2DMultiRange::isInside( const B2DTuple& rTuple ) const
{
return mpImpl->isInside( rTuple );
}
bool B2DMultiRange::isInside( const B2DRange& rRange ) const
{
return mpImpl->isInside( rRange );
}
bool B2DMultiRange::overlaps( const B2DRange& rRange ) const
{
return mpImpl->overlaps( rRange );
}
void B2DMultiRange::addRange( const B2DRange& rRange )
{
mpImpl->addRange( rRange );
}
B2DRange B2DMultiRange::getBounds() const
{
return mpImpl->getBounds();
}
B2DPolyPolygon B2DMultiRange::getPolyPolygon() const
{
return mpImpl->getPolyPolygon();
}
} // end of namespace basegfx
// eof

View File

@@ -0,0 +1,371 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.cxx,v $
* $Revision: 1.8 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
#include <basegfx/range/b2dpolyrange.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/range/b2drangeclipper.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <boost/bind.hpp>
#include <boost/tuple/tuple.hpp>
#include <algorithm>
#include <vector>
static basegfx::B2VectorOrientation flipOrientation(
basegfx::B2VectorOrientation eOrient)
{
return eOrient == basegfx::ORIENTATION_POSITIVE ?
basegfx::ORIENTATION_NEGATIVE : basegfx::ORIENTATION_POSITIVE;
}
namespace basegfx
{
class ImplB2DPolyRange
{
void updateBounds()
{
maBounds.reset();
std::for_each(maRanges.begin(),
maRanges.end(),
boost::bind(
(void (B2DRange::*)(const B2DRange&))(
&B2DRange::expand),
boost::ref(maBounds),
_1));
}
public:
ImplB2DPolyRange() :
maBounds(),
maRanges(),
maOrient()
{}
explicit ImplB2DPolyRange( const B2DPolyRange::ElementType& rElem ) :
maBounds( boost::get<0>(rElem) ),
maRanges( 1, boost::get<0>(rElem) ),
maOrient( 1, boost::get<1>(rElem) )
{}
explicit ImplB2DPolyRange( const B2DRange& rRange, B2VectorOrientation eOrient ) :
maBounds( rRange ),
maRanges( 1, rRange ),
maOrient( 1, eOrient )
{}
bool operator==(const ImplB2DPolyRange& rRHS) const
{
return maRanges == rRHS.maRanges && maOrient == rRHS.maOrient;
}
sal_uInt32 count() const
{
return maRanges.size();
}
B2DPolyRange::ElementType getElement(sal_uInt32 nIndex) const
{
return boost::make_tuple(maRanges[nIndex],
maOrient[nIndex]);
}
void setElement(sal_uInt32 nIndex, const B2DPolyRange::ElementType& rElement )
{
maRanges[nIndex] = boost::get<0>(rElement);
maOrient[nIndex] = boost::get<1>(rElement);
updateBounds();
}
void setElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient )
{
maRanges[nIndex] = rRange;
maOrient[nIndex] = eOrient;
updateBounds();
}
void insertElement(sal_uInt32 nIndex, const B2DPolyRange::ElementType& rElement, sal_uInt32 nCount)
{
maRanges.insert(maRanges.begin()+nIndex, nCount, boost::get<0>(rElement));
maOrient.insert(maOrient.begin()+nIndex, nCount, boost::get<1>(rElement));
maBounds.expand(boost::get<0>(rElement));
}
void insertElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
{
maRanges.insert(maRanges.begin()+nIndex, nCount, rRange);
maOrient.insert(maOrient.begin()+nIndex, nCount, eOrient);
maBounds.expand(rRange);
}
void appendElement(const B2DPolyRange::ElementType& rElement, sal_uInt32 nCount)
{
maRanges.insert(maRanges.end(), nCount, boost::get<0>(rElement));
maOrient.insert(maOrient.end(), nCount, boost::get<1>(rElement));
maBounds.expand(boost::get<0>(rElement));
}
void appendElement(const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
{
maRanges.insert(maRanges.end(), nCount, rRange);
maOrient.insert(maOrient.end(), nCount, eOrient);
maBounds.expand(rRange);
}
void insertPolyRange(sal_uInt32 nIndex, const ImplB2DPolyRange& rPolyRange)
{
maRanges.insert(maRanges.begin()+nIndex, rPolyRange.maRanges.begin(), rPolyRange.maRanges.end());
maOrient.insert(maOrient.begin()+nIndex, rPolyRange.maOrient.begin(), rPolyRange.maOrient.end());
updateBounds();
}
void appendPolyRange(const ImplB2DPolyRange& rPolyRange)
{
maRanges.insert(maRanges.end(),
rPolyRange.maRanges.begin(),
rPolyRange.maRanges.end());
maOrient.insert(maOrient.end(),
rPolyRange.maOrient.begin(),
rPolyRange.maOrient.end());
updateBounds();
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
maRanges.erase(maRanges.begin()+nIndex,maRanges.begin()+nIndex+nCount);
maOrient.erase(maOrient.begin()+nIndex,maOrient.begin()+nIndex+nCount);
updateBounds();
}
void clear()
{
std::vector<B2DRange> aTmpRanges;
std::vector<B2VectorOrientation> aTmpOrient;
maRanges.swap(aTmpRanges);
maOrient.swap(aTmpOrient);
maBounds.reset();
}
void flip()
{
std::for_each(maOrient.begin(),
maOrient.end(),
boost::bind(
&flipOrientation,
_1));
}
B2DRange getBounds() const
{
return maBounds;
}
template< typename ValueType > bool isInside( const ValueType& rValue ) const
{
if( !maBounds.isInside( rValue ) )
return false;
// cannot use boost::bind here, since isInside is overloaded.
// It is currently not possible to resolve the overload
// by considering one of the other template arguments.
std::vector<B2DRange>::const_iterator aCurr( maRanges.begin() );
const std::vector<B2DRange>::const_iterator aEnd ( maRanges.end() );
while( aCurr != aEnd )
if( aCurr->isInside( rValue ) )
return true;
return false;
}
bool overlaps( const B2DRange& rRange ) const
{
if( !maBounds.overlaps( rRange ) )
return false;
const std::vector<B2DRange>::const_iterator aEnd( maRanges.end() );
return std::find_if( maRanges.begin(),
aEnd,
boost::bind<bool>( boost::mem_fn( &B2DRange::overlaps ),
_1,
boost::cref(rRange) ) ) != aEnd;
}
B2DPolyPolygon solveCrossovers() const
{
return tools::solveCrossovers(maRanges,maOrient);
}
private:
B2DRange maBounds;
std::vector<B2DRange> maRanges;
std::vector<B2VectorOrientation> maOrient;
};
B2DPolyRange::B2DPolyRange() :
mpImpl()
{}
B2DPolyRange::~B2DPolyRange()
{}
B2DPolyRange::B2DPolyRange( const ElementType& rElem ) :
mpImpl( ImplB2DPolyRange( rElem ) )
{}
B2DPolyRange::B2DPolyRange( const B2DRange& rRange, B2VectorOrientation eOrient ) :
mpImpl( ImplB2DPolyRange( rRange, eOrient ) )
{}
B2DPolyRange::B2DPolyRange( const B2DPolyRange& rRange ) :
mpImpl( rRange.mpImpl )
{}
B2DPolyRange& B2DPolyRange::operator=( const B2DPolyRange& rRange )
{
mpImpl = rRange.mpImpl;
return *this;
}
void B2DPolyRange::makeUnique()
{
mpImpl.make_unique();
}
bool B2DPolyRange::operator==(const B2DPolyRange& rRange) const
{
if(mpImpl.same_object(rRange.mpImpl))
return true;
return ((*mpImpl) == (*rRange.mpImpl));
}
bool B2DPolyRange::operator!=(const B2DPolyRange& rRange) const
{
return !(*this == rRange);
}
sal_uInt32 B2DPolyRange::count() const
{
return mpImpl->count();
}
B2DPolyRange::ElementType B2DPolyRange::getElement(sal_uInt32 nIndex) const
{
return mpImpl->getElement(nIndex);
}
void B2DPolyRange::setElement(sal_uInt32 nIndex, const ElementType& rElement )
{
mpImpl->setElement(nIndex, rElement);
}
void B2DPolyRange::setElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient )
{
mpImpl->setElement(nIndex, rRange, eOrient );
}
void B2DPolyRange::insertElement(sal_uInt32 nIndex, const ElementType& rElement, sal_uInt32 nCount)
{
mpImpl->insertElement(nIndex, rElement, nCount );
}
void B2DPolyRange::insertElement(sal_uInt32 nIndex, const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
{
mpImpl->insertElement(nIndex, rRange, eOrient, nCount );
}
void B2DPolyRange::appendElement(const ElementType& rElement, sal_uInt32 nCount)
{
mpImpl->appendElement(rElement, nCount);
}
void B2DPolyRange::appendElement(const B2DRange& rRange, B2VectorOrientation eOrient, sal_uInt32 nCount)
{
mpImpl->appendElement(rRange, eOrient, nCount );
}
void B2DPolyRange::insertPolyRange(sal_uInt32 nIndex, const B2DPolyRange& rRange)
{
mpImpl->insertPolyRange(nIndex, *rRange.mpImpl);
}
void B2DPolyRange::appendPolyRange(const B2DPolyRange& rRange)
{
mpImpl->appendPolyRange(*rRange.mpImpl);
}
void B2DPolyRange::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
mpImpl->remove(nIndex, nCount);
}
void B2DPolyRange::clear()
{
mpImpl->clear();
}
void B2DPolyRange::flip()
{
mpImpl->flip();
}
B2DRange B2DPolyRange::getBounds() const
{
return mpImpl->getBounds();
}
bool B2DPolyRange::isInside( const B2DTuple& rTuple ) const
{
return mpImpl->isInside(rTuple);
}
bool B2DPolyRange::isInside( const B2DRange& rRange ) const
{
return mpImpl->isInside(rRange);
}
bool B2DPolyRange::overlaps( const B2DRange& rRange ) const
{
return mpImpl->overlaps(rRange);
}
B2DPolyPolygon B2DPolyRange::solveCrossovers() const
{
return mpImpl->solveCrossovers();
}
} // end of namespace basegfx
// eof

View File

@@ -0,0 +1,950 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: b2dmultirange.cxx,v $
* $Revision: 1.8 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
#include <rtl/math.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/range/b2dpolyrange.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <o3tl/vector_pool.hxx>
#include <boost/bind.hpp>
#include <boost/utility.hpp>
#include <algorithm>
#include <deque>
#include <list>
namespace basegfx
{
namespace
{
// Generating a poly-polygon from a bunch of rectangles
//
// Helper functionality for sweep-line algorithm
// ====================================================
typedef std::vector<B2DRange> VectorOfRanges;
class ImplPolygon;
typedef o3tl::vector_pool<ImplPolygon> VectorOfPolygons;
/** This class represents an active edge
As the sweep line traverses across the overall area,
rectangle edges parallel to it generate events, and
rectangle edges orthogonal to it generate active
edges. This class represents the latter.
*/
class ActiveEdge
{
public:
/** The two possible active rectangle edges differ by one
coordinate value - the upper edge has the lower, the
lower edge the higher value.
*/
enum EdgeType {
/// edge with lower coordinate value
UPPER=0,
/// edge with higher coordinate value
LOWER=1
};
enum EdgeDirection {
/// edge proceeds to the left
PROCEED_LEFT=0,
/// edge proceeds to the right
PROCEED_RIGHT=1
};
/** Create active edge
@param rRect
Rectangle this edge is part of
@param fInvariantCoord
The invariant ccordinate value of this edge
@param eEdgeType
Is fInvariantCoord the lower or the higher value, for
this rect?
*/
ActiveEdge( const B2DRectangle& rRect,
const double& fInvariantCoord,
std::ptrdiff_t nPolyIdx,
EdgeType eEdgeType,
EdgeDirection eEdgeDirection ) :
mfInvariantCoord(fInvariantCoord),
mpAssociatedRect( &rRect ),
mnPolygonIdx( nPolyIdx ),
meEdgeType( eEdgeType ),
meEdgeDirection( eEdgeDirection )
{}
double getInvariantCoord() const { return mfInvariantCoord; }
const B2DRectangle& getRect() const { return *mpAssociatedRect; }
std::ptrdiff_t getTargetPolygonIndex() const { return mnPolygonIdx; }
void setTargetPolygonIndex( std::ptrdiff_t nIdx ) { mnPolygonIdx = nIdx; }
EdgeType getEdgeType() const { return meEdgeType; }
EdgeDirection getEdgeDirection() const { return meEdgeDirection; }
/// For STL sort
bool operator<( const ActiveEdge& rRHS ) const { return mfInvariantCoord < rRHS.mfInvariantCoord; }
private:
/** The invariant coordinate value of this edge (e.g. the
common y value, for a horizontal edge)
*/
double mfInvariantCoord;
/** Associated rectangle
This on the one hand saves some storage space (the
vector of rectangles is persistent, anyway), and on
the other hand provides an identifier to match active
edges and x events (see below)
Ptr because class needs to be assignable
*/
const B2DRectangle* mpAssociatedRect;
/** Index of the polygon this edge is currently involved
with.
Note that this can change for some kinds of edge
intersection, as the algorithm tends to swap
associated polygons there.
-1 denotes no assigned polygon
*/
std::ptrdiff_t mnPolygonIdx;
/// 'upper' or 'lower' edge of original rectangle.
EdgeType meEdgeType;
/// 'left' or 'right'
EdgeDirection meEdgeDirection;
};
// Needs to be list - various places hold ptrs to elements
typedef std::list< ActiveEdge > ListOfEdges;
/** Element of the sweep line event list
As the sweep line traverses across the overall area,
rectangle edges parallel to it generate events, and
rectangle edges orthogonal to it generate active
edges. This class represents the former.
The class defines an element of the sweep line list. The
sweep line's position jumps in steps defined by the
coordinates of the sorted SweepLineEvent entries.
*/
class SweepLineEvent
{
public:
/** The two possible sweep line rectangle edges differ by
one coordinate value - the starting edge has the
lower, the finishing edge the higher value.
*/
enum EdgeType {
/// edge with lower coordinate value
STARTING_EDGE=0,
/// edge with higher coordinate value
FINISHING_EDGE=1
};
/** The two possible sweep line directions
*/
enum EdgeDirection {
PROCEED_UP=0,
PROCEED_DOWN=1
};
/** Create sweep line event
@param fPos
Coordinate position of the event
@param rRect
Rectangle this event is generated for.
@param eEdgeType
Is fPos the lower or the higher value, for the
rectangle this event is generated for?
*/
SweepLineEvent( double fPos,
const B2DRectangle& rRect,
EdgeType eEdgeType,
EdgeDirection eDirection) :
mfPos( fPos ),
mpAssociatedRect( &rRect ),
meEdgeType( eEdgeType ),
meEdgeDirection( eDirection )
{}
double getPos() const { return mfPos; }
const B2DRectangle& getRect() const { return *mpAssociatedRect; }
EdgeType getEdgeType() const { return meEdgeType; }
EdgeDirection getEdgeDirection() const { return meEdgeDirection; }
/// For STL sort
bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; }
private:
/// position of the event, in the direction of the line sweep
double mfPos;
/** Rectangle this event is generated for
This on the one hand saves some storage space (the
vector of rectangles is persistent, anyway), and on
the other hand provides an identifier to match active
edges and events (see below)
Ptr because class needs to be assignable
*/
const B2DRectangle* mpAssociatedRect;
/// 'upper' or 'lower' edge of original rectangle.
EdgeType meEdgeType;
/// 'up' or 'down'
EdgeDirection meEdgeDirection;
};
typedef std::vector< SweepLineEvent > VectorOfEvents;
/** Smart point container for B2DMultiRange::getPolyPolygon()
This class provides methods needed only here, and is used
as a place to store some additional information per
polygon. Also, most of the intersection logic is
implemented here.
*/
class ImplPolygon
{
public:
/** Create polygon
*/
ImplPolygon() :
mpLeadingRightEdge(NULL),
mnIdx(-1),
maPoints(),
mbIsFinished(false)
{
// completely ad-hoc. but what the hell.
maPoints.reserve(11);
}
void setPolygonPoolIndex( std::ptrdiff_t nIdx ) { mnIdx = nIdx; }
bool isFinished() const { return mbIsFinished; }
/// Add point to the end of the existing points
void append( const B2DPoint& rPoint )
{
OSL_PRECOND( maPoints.empty() ||
maPoints.back().getX() == rPoint.getX() ||
maPoints.back().getY() == rPoint.getY(),
"ImplPolygon::append(): added point violates 90 degree line angle constraint!" );
if( maPoints.empty() ||
maPoints.back() != rPoint )
{
// avoid duplicate points
maPoints.push_back( rPoint );
}
}
/** Perform the intersection of this polygon with an
active edge.
@param rEvent
The vertical line event that generated the
intersection
@param rActiveEdge
The active edge that generated the intersection
@param rPolygonPool
Polygon pool, we sometimes need to allocate a new one
@param bIsFinishingEdge
True, when this is hitting the last edge of the
vertical sweep - every vertical sweep starts and ends
with upper and lower edge of the _same_ rectangle.
@return the new current polygon (that's the one
processing must proceed with, when going through the
list of upcoming active edges).
*/
std::ptrdiff_t intersect( SweepLineEvent& rEvent,
ActiveEdge& rActiveEdge,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes,
bool isFinishingEdge )
{
OSL_PRECOND( !isFinished(),
"ImplPolygon::intersect(): called on already finished polygon!" );
OSL_PRECOND( !isFinishingEdge
|| (isFinishingEdge && &rEvent.getRect() == &rActiveEdge.getRect()),
"ImplPolygon::intersect(): inconsistent ending!" );
const B2DPoint aIntersectionPoint( rEvent.getPos(),
rActiveEdge.getInvariantCoord() );
// intersection point, goes to our polygon
// unconditionally
append(aIntersectionPoint);
const bool isSweepLineEnteringRect(
rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE);
if( isFinishingEdge )
{
if( isSweepLineEnteringRect )
handleFinalOwnRightEdge(rActiveEdge);
else
handleFinalOwnLeftEdge(rActiveEdge,
rPolygonPool,
rRes);
// we're done with this rect & sweep line
return -1;
}
else if( metOwnEdge(rEvent,rActiveEdge) )
{
handleInitialOwnEdge(rEvent, rActiveEdge);
// point already added, all init done, continue
// with same poly
return mnIdx;
}
else
{
OSL_ENSURE( rActiveEdge.getTargetPolygonIndex() != -1,
"ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" );
const bool isHittingLeftEdge(
rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
if( isHittingLeftEdge )
return handleComplexLeftEdge(rActiveEdge,
aIntersectionPoint,
rPolygonPool,
rRes);
else
return handleComplexRightEdge(rActiveEdge,
aIntersectionPoint,
rPolygonPool);
}
}
private:
std::ptrdiff_t getPolygonPoolIndex() const { return mnIdx; }
void handleInitialOwnEdge(SweepLineEvent& rEvent,
ActiveEdge& rActiveEdge)
{
const bool isActiveEdgeProceedLeft(
rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT);
const bool isSweepLineEnteringRect(
rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE);
(void)isActiveEdgeProceedLeft;
(void)isSweepLineEnteringRect;
OSL_ENSURE( isSweepLineEnteringRect == isActiveEdgeProceedLeft,
"ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" );
OSL_ENSURE( isSweepLineEnteringRect ||
mpLeadingRightEdge == &rActiveEdge,
"ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" );
}
void handleFinalOwnRightEdge(ActiveEdge& rActiveEdge)
{
OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT,
"ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" );
rActiveEdge.setTargetPolygonIndex(mnIdx);
mpLeadingRightEdge = &rActiveEdge;
}
void handleFinalOwnLeftEdge(ActiveEdge& rActiveEdge,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes)
{
OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT,
"ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" );
const bool isHittingOurTail(
rActiveEdge.getTargetPolygonIndex() == mnIdx);
if( isHittingOurTail )
finish(rRes); // just finish. no fuss.
else
{
// temp poly hits final left edge
const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
// active edge's polygon has points
// already. ours need to go in front of them.
maPoints.insert(maPoints.end(),
rTmp.maPoints.begin(),
rTmp.maPoints.end());
// adjust leading edges, we're switching the polygon
ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
mpLeadingRightEdge = pFarEdge;
pFarEdge->setTargetPolygonIndex(mnIdx);
// nTmpIdx is an empty shell, get rid of it
rPolygonPool.free(nTmpIdx);
}
}
std::ptrdiff_t handleComplexLeftEdge(ActiveEdge& rActiveEdge,
const B2DPoint& rIntersectionPoint,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes)
{
const bool isHittingOurTail(
rActiveEdge.getTargetPolygonIndex() == mnIdx);
if( isHittingOurTail )
{
finish(rRes);
// so "this" is done - need new polygon to collect
// further points
const std::ptrdiff_t nIdxNewPolygon=rPolygonPool.alloc();
rPolygonPool.get(nIdxNewPolygon).setPolygonPoolIndex(nIdxNewPolygon);
rPolygonPool.get(nIdxNewPolygon).append(rIntersectionPoint);
rActiveEdge.setTargetPolygonIndex(nIdxNewPolygon);
return nIdxNewPolygon;
}
else
{
const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
// active edge's polygon has points
// already. ours need to go in front of them.
maPoints.insert(maPoints.end(),
rTmp.maPoints.begin(),
rTmp.maPoints.end());
rTmp.maPoints.clear();
rTmp.append(rIntersectionPoint);
// adjust leading edges, we're switching the polygon
ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge;
ActiveEdge* const pNearEdge=&rActiveEdge;
rTmp.mpLeadingRightEdge = NULL;
pNearEdge->setTargetPolygonIndex(nTmpIdx);
mpLeadingRightEdge = pFarEdge;
pFarEdge->setTargetPolygonIndex(mnIdx);
return nTmpIdx;
}
}
std::ptrdiff_t handleComplexRightEdge(ActiveEdge& rActiveEdge,
const B2DPoint& rIntersectionPoint,
VectorOfPolygons& rPolygonPool)
{
const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex();
ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx);
rTmp.append(rIntersectionPoint);
rActiveEdge.setTargetPolygonIndex(mnIdx);
mpLeadingRightEdge = &rActiveEdge;
rTmp.mpLeadingRightEdge = NULL;
return nTmpIdx;
}
/// True when sweep line hits our own active edge
bool metOwnEdge(const SweepLineEvent& rEvent,
ActiveEdge& rActiveEdge)
{
const bool bHitOwnEdge=&rEvent.getRect() == &rActiveEdge.getRect();
return bHitOwnEdge;
}
/// Retrieve B2DPolygon from this object
B2DPolygon getPolygon() const
{
B2DPolygon aRes;
std::for_each( maPoints.begin(),
maPoints.end(),
boost::bind(
&B2DPolygon::append,
boost::ref(aRes),
_1,
1 ) );
aRes.setClosed( true );
return aRes;
}
/** Finish this polygon, push to result set.
*/
void finish(B2DPolyPolygon& rRes)
{
OSL_PRECOND( maPoints.empty() ||
maPoints.front().getX() == maPoints.back().getX() ||
maPoints.front().getY() == maPoints.back().getY(),
"ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" );
mbIsFinished = true;
mpLeadingRightEdge = NULL;
rRes.append(getPolygon());
}
/** Refers to the current leading edge element of this
polygon, or NULL. The leading edge denotes the 'front'
of the polygon vertex sequence, i.e. the coordinates
at the polygon's leading edge are returned from
maPoints.front()
*/
ActiveEdge* mpLeadingRightEdge;
/// current index into vector pool
std::ptrdiff_t mnIdx;
/// Container for the actual polygon points
std::vector<B2DPoint> maPoints;
/// When true, this polygon is 'done', i.e. nothing must be added anymore.
bool mbIsFinished;
};
/** Init sweep line event list
This method fills the event list with the sweep line
events generated from the input rectangles, and sorts them
with increasing x.
*/
void setupSweepLineEventListFromRanges( VectorOfEvents& o_rEventVector,
const std::vector<B2DRange>& rRanges,
const std::vector<B2VectorOrientation>& rOrientations )
{
// we need exactly 2*rectVec.size() events: one for the
// left, and one for the right edge of each rectangle
o_rEventVector.clear();
o_rEventVector.reserve( 2*rRanges.size() );
// generate events
// ===============
// first pass: add all left edges in increasing order
std::vector<B2DRange>::const_iterator aCurrRect=rRanges.begin();
std::vector<B2VectorOrientation>::const_iterator aCurrOrientation=rOrientations.begin();
const std::vector<B2DRange>::const_iterator aEnd=rRanges.end();
const std::vector<B2VectorOrientation>::const_iterator aEndOrientation=rOrientations.end();
while( aCurrRect != aEnd && aCurrOrientation != aEndOrientation )
{
const B2DRectangle& rCurrRect( *aCurrRect++ );
o_rEventVector.push_back(
SweepLineEvent( rCurrRect.getMinX(),
rCurrRect,
SweepLineEvent::STARTING_EDGE,
(*aCurrOrientation++) == ORIENTATION_POSITIVE ?
SweepLineEvent::PROCEED_UP : SweepLineEvent::PROCEED_DOWN) );
}
// second pass: add all right edges in reversed order
std::vector<B2DRange>::const_reverse_iterator aCurrRectR=rRanges.rbegin();
std::vector<B2VectorOrientation>::const_reverse_iterator aCurrOrientationR=rOrientations.rbegin();
const std::vector<B2DRange>::const_reverse_iterator aEndR=rRanges.rend();
const std::vector<B2VectorOrientation>::const_reverse_iterator aEndOrientationR=rOrientations.rend();
while( aCurrRectR != aEndR )
{
const B2DRectangle& rCurrRect( *aCurrRectR++ );
o_rEventVector.push_back(
SweepLineEvent( rCurrRect.getMaxX(),
rCurrRect,
SweepLineEvent::FINISHING_EDGE,
(*aCurrOrientationR++) == ORIENTATION_POSITIVE ?
SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP ) );
}
// sort events
// ===========
// since we use stable_sort, the order of events with the
// same x value will not change. The elaborate two-pass
// add above thus ensures, that for each two rectangles
// with similar left and right x coordinates, the
// rectangle whose left event comes first will have its
// right event come last. This is advantageous for the
// clip algorithm below, see handleRightEdgeCrossing().
// TODO(P3): Use radix sort (from
// b2dpolypolygonrasterconverter, or have your own
// templatized version).
std::stable_sort( o_rEventVector.begin(),
o_rEventVector.end() );
}
/** Insert two active edge segments for the given rectangle.
This method creates two active edge segments from the
given rect, and inserts them into the active edge list,
such that this stays sorted (if it was before).
@param io_rEdgeList
Active edge list to insert into
@param io_rPolygons
Vector of polygons. Each rectangle added creates one
tentative result polygon in this vector, and the edge list
entries holds a reference to that polygon (this _requires_
that the polygon vector does not reallocate, i.e. it must
have at least the maximal number of rectangles reserved)
@param o_CurrentPolygon
The then-current polygon when processing this sweep line
event
@param rCurrEvent
The actual event that caused this call
*/
void createActiveEdgesFromStartEvent( ListOfEdges& io_rEdgeList,
VectorOfPolygons& io_rPolygonPool,
SweepLineEvent& rCurrEvent )
{
ListOfEdges aNewEdges;
const B2DRectangle& rRect=rCurrEvent.getRect();
const bool bGoesDown=rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN;
// start event - new rect starts here, needs polygon to
// collect points into
const std::ptrdiff_t nIdxPolygon=io_rPolygonPool.alloc();
io_rPolygonPool.get(nIdxPolygon).setPolygonPoolIndex(nIdxPolygon);
// upper edge
aNewEdges.push_back(
ActiveEdge(
rRect,
rRect.getMinY(),
bGoesDown ? nIdxPolygon : -1,
ActiveEdge::UPPER,
bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT) );
// lower edge
aNewEdges.push_back(
ActiveEdge(
rRect,
rRect.getMaxY(),
bGoesDown ? -1 : nIdxPolygon,
ActiveEdge::LOWER,
bGoesDown ? ActiveEdge::PROCEED_RIGHT : ActiveEdge::PROCEED_LEFT ) );
// furthermore, have to respect a special tie-breaking
// rule here, for edges which share the same y value:
// newly added upper edges must be inserted _before_ any
// other edge with the same y value, and newly added lower
// edges must be _after_ all other edges with the same
// y. This ensures that the left vertical edge processing
// below encounters the upper edge of the current rect
// first, and the lower edge last, which automatically
// starts and finishes this rect correctly (as only then,
// the polygon will have their associated active edges
// set).
const double nMinY( rRect.getMinY() );
const double nMaxY( rRect.getMaxY() );
ListOfEdges::iterator aCurr( io_rEdgeList.begin() );
const ListOfEdges::iterator aEnd ( io_rEdgeList.end() );
while( aCurr != aEnd )
{
const double nCurrY( aCurr->getInvariantCoord() );
if( nCurrY >= nMinY &&
aNewEdges.size() == 2 ) // only add, if not yet done.
{
// insert upper edge _before_ aCurr. Thus, it will
// be the first entry for a range of equal y
// values. Using splice here, since we hold
// references to the moved list element!
io_rEdgeList.splice( aCurr,
aNewEdges,
aNewEdges.begin() );
}
if( nCurrY > nMaxY )
{
// insert lower edge _before_ aCurr. Thus, it will
// be the last entry for a range of equal y values
// (aCurr is the first entry strictly larger than
// nMaxY). Using splice here, since we hold
// references to the moved list element!
io_rEdgeList.splice( aCurr,
aNewEdges,
aNewEdges.begin() );
// done with insertion, can early-exit here.
return;
}
++aCurr;
}
// append remainder of aNewList (might still contain 2 or
// 1 elements, depending of the contents of io_rEdgeList).
io_rEdgeList.splice( aCurr,
aNewEdges );
}
inline bool isSameRect(ActiveEdge& rEdge,
const basegfx::B2DRange& rRect)
{
return &rEdge.getRect() == &rRect;
}
// wow what a hack. necessary because stl's list::erase does
// not eat reverse_iterator
template<typename Cont, typename Iter> Iter eraseFromList(Cont&, Iter);
template<> inline ListOfEdges::iterator eraseFromList(
ListOfEdges& rList, ListOfEdges::iterator aIter)
{
return rList.erase(aIter);
}
template<> inline ListOfEdges::reverse_iterator eraseFromList(
ListOfEdges& rList, ListOfEdges::reverse_iterator aIter)
{
return ListOfEdges::reverse_iterator(
rList.erase(boost::prior(aIter.base())));
}
template<int bPerformErase,
typename Iterator> inline void processActiveEdges(
Iterator first,
Iterator last,
ListOfEdges& rActiveEdgeList,
SweepLineEvent& rCurrEvent,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes )
{
const basegfx::B2DRange& rCurrRect=rCurrEvent.getRect();
// fast-forward to rCurrEvent's first active edge (holds
// for both starting and finishing sweep line events, a
// rect is regarded _outside_ any rects whose events have
// started earlier
first = std::find_if(first, last,
boost::bind(
&isSameRect,
_1,
boost::cref(rCurrRect)));
if(first == last)
return;
int nCount=0;
std::ptrdiff_t nCurrPolyIdx=-1;
while(first != last)
{
if( nCurrPolyIdx == -1 )
nCurrPolyIdx=first->getTargetPolygonIndex();
OSL_ASSERT(nCurrPolyIdx != -1);
// second encounter of my rect -> second edge
// encountered, done
const bool bExit=
nCount &&
isSameRect(*first,
rCurrRect);
// deal with current active edge
nCurrPolyIdx =
rPolygonPool.get(nCurrPolyIdx).intersect(
rCurrEvent,
*first,
rPolygonPool,
rRes,
bExit);
// prune upper & lower active edges, if requested
if( bPerformErase && (bExit || !nCount) )
first = eraseFromList(rActiveEdgeList,first);
else
++first;
// delayed exit, had to prune first
if( bExit )
return;
++nCount;
}
}
template<int bPerformErase> inline void processActiveEdgesTopDown(
SweepLineEvent& rCurrEvent,
ListOfEdges& rActiveEdgeList,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes )
{
processActiveEdges<bPerformErase>(
rActiveEdgeList. begin(),
rActiveEdgeList. end(),
rActiveEdgeList,
rCurrEvent,
rPolygonPool,
rRes);
}
template<int bPerformErase> inline void processActiveEdgesBottomUp(
SweepLineEvent& rCurrEvent,
ListOfEdges& rActiveEdgeList,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes )
{
processActiveEdges<bPerformErase>(
rActiveEdgeList. rbegin(),
rActiveEdgeList. rend(),
rActiveEdgeList,
rCurrEvent,
rPolygonPool,
rRes);
}
enum{ NoErase=0, PerformErase=1 };
void handleStartingEdge( SweepLineEvent& rCurrEvent,
ListOfEdges& rActiveEdgeList,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes)
{
// inject two new active edges for rect
createActiveEdgesFromStartEvent( rActiveEdgeList,
rPolygonPool,
rCurrEvent );
if( SweepLineEvent::PROCEED_DOWN == rCurrEvent.getEdgeDirection() )
processActiveEdgesTopDown<NoErase>(
rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
else
processActiveEdgesBottomUp<NoErase>(
rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
}
void handleFinishingEdge( SweepLineEvent& rCurrEvent,
ListOfEdges& rActiveEdgeList,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes)
{
if( SweepLineEvent::PROCEED_DOWN == rCurrEvent.getEdgeDirection() )
processActiveEdgesTopDown<PerformErase>(
rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
else
processActiveEdgesBottomUp<PerformErase>(
rCurrEvent, rActiveEdgeList, rPolygonPool, rRes);
}
inline void handleSweepLineEvent( SweepLineEvent& rCurrEvent,
ListOfEdges& rActiveEdgeList,
VectorOfPolygons& rPolygonPool,
B2DPolyPolygon& rRes)
{
if( SweepLineEvent::STARTING_EDGE == rCurrEvent.getEdgeType() )
handleStartingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
else
handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes);
}
}
namespace tools
{
B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges,
const std::vector<B2VectorOrientation>& rOrientations)
{
// sweep-line algorithm to generate a poly-polygon
// from a bunch of rectangles
// ===============================================
//
// This algorithm uses the well-known sweep line
// concept, explained in every good text book about
// computational geometry.
//
// We start with creating two structures for every
// rectangle, one representing the left x coordinate,
// one representing the right x coordinate (and both
// referencing the original rect). These structs are
// sorted with increasing x coordinates.
//
// Then, we start processing the resulting list from
// the beginning. Every entry in the list defines a
// point in time of the line sweeping from left to
// right across all rectangles.
VectorOfEvents aSweepLineEvents;
setupSweepLineEventListFromRanges( aSweepLineEvents,
rRanges,
rOrientations );
B2DPolyPolygon aRes;
VectorOfPolygons aPolygonPool;
ListOfEdges aActiveEdgeList;
// sometimes not enough, but a usable compromise
aPolygonPool.reserve( rRanges.size() );
std::for_each( aSweepLineEvents.begin(),
aSweepLineEvents.end(),
boost::bind(
&handleSweepLineEvent,
_1,
boost::ref(aActiveEdgeList),
boost::ref(aPolygonPool),
boost::ref(aRes)) );
return aRes;
}
}
}

View File

@@ -47,7 +47,8 @@ SLOFILES= \
$(SLO)$/b1drange.obj \
$(SLO)$/b2drange.obj \
$(SLO)$/b2xrange.obj \
$(SLO)$/b2dmultirange.obj \
$(SLO)$/b2dpolyrange.obj \
$(SLO)$/b2drangeclipper.obj \
$(SLO)$/b3drange.obj
# --- Targets ----------------------------------

View File

@@ -41,7 +41,9 @@
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/curve/b2dbeziertools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/range/b2dmultirange.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/range/b2dpolyrange.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/color/bcolor.hxx>
#include <basegfx/color/bcolortools.hxx>
@@ -56,214 +58,6 @@ using namespace ::basegfx;
namespace basegfx2d
{
/// Gets a random ordinal [0,n)
inline double getRandomOrdinal( const ::std::size_t n )
{
return double(n) * rand() / (RAND_MAX + 1.0);
}
class b2dmultirange : public CppUnit::TestFixture
{
private:
B2DMultiRange aDisjunctRanges;
B2DMultiRange aEqualRanges;
B2DMultiRange aIntersectionN;
B2DMultiRange aIntersectionE;
B2DMultiRange aIntersectionS;
B2DMultiRange aIntersectionW;
B2DMultiRange aIntersectionNE;
B2DMultiRange aIntersectionSE;
B2DMultiRange aIntersectionSW;
B2DMultiRange aIntersectionNW;
B2DMultiRange aRingIntersection;
B2DMultiRange aComplexIntersections;
B2DMultiRange aRandomIntersections;
public:
// initialise your test code values here.
void setUp()
{
B2DRange aCenter(1.0, 1.0, -1.0, -1.0);
B2DRange aOffside(9.0, 9.0, 11.0, 11.0);
B2DRange aNorth(1.0, 0.0, -1.0, -2.0);
B2DRange aSouth(1.0, 2.0, -1.0, 0.0);
B2DRange aEast(0.0, 1.0, 2.0, -1.0);
B2DRange aWest(-2.0, 1.0, 0.0, -1.0);
B2DRange aNorthEast(0.0, 0.0, 2.0, -2.0);
B2DRange aSouthEast(0.0, 0.0, 2.0, 2.0);
B2DRange aSouthWest(0.0, 0.0, -2.0, 2.0);
B2DRange aNorthWest(0.0, 0.0, -2.0, -2.0);
B2DRange aNorth2(-1.5, 0.5, 1.5, 3.5);
B2DRange aSouth2(-1.5, -0.5, 1.5, -3.5);
B2DRange aEast2 (0.5, -1.5, 3.5, 1.5);
B2DRange aWest2 (-0.5, -1.5,-3.5, 1.5);
::std::ofstream output("multirange_testcases.gnuplot");
DebugPlotter aPlotter( "multirange testcases",
output );
aPlotter.plot( aCenter, "center" );
aPlotter.plot( aOffside, "offside" );
aPlotter.plot( aNorth, "north" );
aPlotter.plot( aSouth, "south" );
aPlotter.plot( aEast, "east" );
aPlotter.plot( aWest, "west" );
aPlotter.plot( aNorthEast, "northeast" );
aPlotter.plot( aSouthEast, "southeast" );
aPlotter.plot( aSouthWest, "southwest" );
aPlotter.plot( aNorthWest, "northwest" );
aDisjunctRanges.addRange( aCenter );
aDisjunctRanges.addRange( aOffside );
aEqualRanges.addRange( aCenter );
aEqualRanges.addRange( aCenter );
aIntersectionN.addRange( aCenter );
aIntersectionN.addRange( aNorth );
aIntersectionE.addRange( aCenter );
aIntersectionE.addRange( aEast );
aIntersectionS.addRange( aCenter );
aIntersectionS.addRange( aSouth );
aIntersectionW.addRange( aCenter );
aIntersectionW.addRange( aWest );
aIntersectionNE.addRange( aCenter );
aIntersectionNE.addRange( aNorthEast );
aIntersectionSE.addRange( aCenter );
aIntersectionSE.addRange( aSouthEast );
aIntersectionSW.addRange( aCenter );
aIntersectionSW.addRange( aSouthWest );
aIntersectionNW.addRange( aCenter );
aIntersectionNW.addRange( aNorthWest );
aRingIntersection.addRange( aNorth2 );
aRingIntersection.addRange( aEast2 );
aRingIntersection.addRange( aSouth2 );
aRingIntersection.addRange( aWest2 );
aComplexIntersections.addRange( aCenter );
aComplexIntersections.addRange( aOffside );
aComplexIntersections.addRange( aCenter );
aComplexIntersections.addRange( aNorth );
aComplexIntersections.addRange( aEast );
aComplexIntersections.addRange( aSouth );
aComplexIntersections.addRange( aWest );
aComplexIntersections.addRange( aNorthEast );
aComplexIntersections.addRange( aSouthEast );
aComplexIntersections.addRange( aSouthWest );
aComplexIntersections.addRange( aNorthWest );
/*
for( int i=0; i<10; ++i )
{
B2DRange aRandomRange(
getRandomOrdinal( 10 ),
getRandomOrdinal( 10 ),
getRandomOrdinal( 10 ),
getRandomOrdinal( 10 ) );
aRandomIntersections.addRange( aRandomRange );
}
*/
}
void tearDown()
{
}
::basegfx::B2DPolyPolygon shiftPoly( int nCount,
const ::basegfx::B2DPolyPolygon& rPoly )
{
B2DHomMatrix aMatrix;
aMatrix.translate( nCount*4.0,
10.0-nCount*2.0 );
::basegfx::B2DPolyPolygon aRes( rPoly );
aRes.transform( aMatrix );
return aRes;
}
void getPolyPolygon()
{
::std::ofstream output("multirange_getpolypolygon.gnuplot");
DebugPlotter aPlotter( "multirange getPolyPolygon",
output );
B2DPolyPolygon result;
aPlotter.plot( shiftPoly(
0,
aDisjunctRanges.getPolyPolygon() ),
"disjunct" );
aPlotter.plot( shiftPoly(
1,
aEqualRanges.getPolyPolygon() ),
"equal" );
aPlotter.plot( shiftPoly(
2,
aIntersectionN.getPolyPolygon() ),
"intersectionN" );
aPlotter.plot( shiftPoly(
3,
aIntersectionE.getPolyPolygon() ),
"intersectionE" );
aPlotter.plot( shiftPoly(
4,
aIntersectionS.getPolyPolygon() ),
"intersectionS" );
aPlotter.plot( shiftPoly(
5,
aIntersectionW.getPolyPolygon() ),
"intersectionW" );
aPlotter.plot( shiftPoly(
6,
aIntersectionNE.getPolyPolygon() ),
"intersectionNE" );
aPlotter.plot( shiftPoly(
7,
aIntersectionSE.getPolyPolygon() ),
"intersectionSE" );
aPlotter.plot( shiftPoly(
8,
aIntersectionSW.getPolyPolygon() ),
"intersectionSW" );
aPlotter.plot( shiftPoly(
9,
aIntersectionNW.getPolyPolygon() ),
"intersectionNW" );
aPlotter.plot( shiftPoly(
10,
aRingIntersection.getPolyPolygon() ),
"intersection ring" );
aPlotter.plot( shiftPoly(
11,
aComplexIntersections.getPolyPolygon() ),
"intersection complex" );
aPlotter.plot( shiftPoly(
12,
aRandomIntersections.getPolyPolygon() ),
"intersection random" );
CPPUNIT_ASSERT_MESSAGE("getPolyPolygon", true );
}
// Change the following lines only, if you add, remove or rename
// member functions of the current class,
// because these macros are need by auto register mechanism.
CPPUNIT_TEST_SUITE(b2dmultirange);
CPPUNIT_TEST(getPolyPolygon);
CPPUNIT_TEST_SUITE_END();
}; // class b2dmultirange
class b2dsvgdimpex : public CppUnit::TestFixture
{
@@ -505,6 +299,39 @@ public:
CPPUNIT_TEST_SUITE_END();
}; // class b2dsvgdimpex
class b2dpolyrange : public CppUnit::TestFixture
{
private:
public:
void setUp()
{}
void tearDown()
{}
void check()
{
B2DPolyRange aRange;
aRange.appendElement(B2DRange(0,0,1,1),ORIENTATION_POSITIVE);
aRange.appendElement(B2DRange(2,2,3,3),ORIENTATION_POSITIVE);
CPPUNIT_ASSERT_MESSAGE("simple poly range - count",
aRange.count() == 2);
CPPUNIT_ASSERT_MESSAGE("simple poly range - first element",
aRange.getElement(0).head == B2DRange(0,0,1,1));
CPPUNIT_ASSERT_MESSAGE("simple poly range - second element",
aRange.getElement(1).head == B2DRange(2,2,3,3));
}
// Change the following lines only, if you add, remove or rename
// member functions of the current class,
// because these macros are need by auto register mechanism.
CPPUNIT_TEST_SUITE(b2dpolyrange);
CPPUNIT_TEST(check);
CPPUNIT_TEST_SUITE_END();
};
class b2dbeziertools : public CppUnit::TestFixture
{
private:
@@ -1618,8 +1445,9 @@ public:
}; // class b2dvector
// -----------------------------------------------------------------------------
//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dmultirange, "basegfx2d");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dsvgdimpex, "basegfx2d");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dpolyrange, "basegfx2d");
//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dbeziertools, "basegfx2d");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dcubicbezier, "basegfx2d");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dhommatrix, "basegfx2d");

426
basegfx/test/boxclipper.cxx Normal file

File diff suppressed because one or more lines are too long

View File

@@ -85,6 +85,10 @@ SLOFILES=$(SHL1OBJS)
.INCLUDE : target.mk
.INCLUDE : _cppunit.mk
.IF "$(verbose)"!="" || "$(VERBOSE)"!=""
CDEFS+= -DVERBOSE
.ENDIF
# --- Enable testshl2 execution in normal build ------------------------
$(MISC)$/unittest_succeeded : $(SHL1TARGETN)