//C- -*- C++ -*- //C- ------------------------------------------------------------------- //C- DjVuLibre-3.5 //C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. //C- Copyright (c) 2001 AT&T //C- //C- This software is subject to, and may be distributed under, the //C- GNU General Public License, Version 2. The license should have //C- accompanied the software or you may obtain a copy of the license //C- from the Free Software Foundation at http://www.fsf.org . //C- //C- This program is distributed in the hope that it will be useful, //C- but WITHOUT ANY WARRANTY; without even the implied warranty of //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //C- GNU General Public License for more details. //C- //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library //C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech //C- Software authorized us to replace the original DjVu(r) Reference //C- Library notice by the following text (see doc/lizard2002.djvu): //C- //C- ------------------------------------------------------------------ //C- | DjVu (r) Reference Library (v. 3.5) //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. //C- | The DjVu Reference Library is protected by U.S. Pat. No. //C- | 6,058,214 and patents pending. //C- | //C- | This software is subject to, and may be distributed under, the //C- | GNU General Public License, Version 2. The license should have //C- | accompanied the software or you may obtain a copy of the license //C- | from the Free Software Foundation at http://www.fsf.org . //C- | //C- | The computer code originally released by LizardTech under this //C- | license and unmodified by other parties is deemed "the LIZARDTECH //C- | ORIGINAL CODE." Subject to any third party intellectual property //C- | claims, LizardTech grants recipient a worldwide, royalty-free, //C- | non-exclusive license to make, use, sell, or otherwise dispose of //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU //C- | General Public License. This grant only confers the right to //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to //C- | the extent such infringement is reasonably necessary to enable //C- | recipient to make, have made, practice, sell, or otherwise dispose //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to //C- | any greater extent that may be necessary to utilize further //C- | modifications or combinations. //C- | //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. //C- +------------------------------------------------------------------ // // $Id: GMapAreas.cpp,v 1.9 2004/05/05 15:12:42 leonb Exp $ // $Name: release_3_5_15 $ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if NEED_GNUG_PRAGMAS # pragma implementation #endif #include "GMapAreas.h" #include "GException.h" #include "debug.h" #include #include #ifdef HAVE_NAMESPACES namespace DJVU { # ifdef NOT_DEFINED // Just to fool emacs c++ mode } #endif #endif /**************************************************************************** ***************************** GMapArea definition *************************** ****************************************************************************/ const char GMapArea::MAPAREA_TAG[] = "maparea"; const char GMapArea::RECT_TAG[] = "rect"; const char GMapArea::POLY_TAG[] = "poly"; const char GMapArea::OVAL_TAG[] = "oval"; const char GMapArea::NO_BORDER_TAG[] = "none"; const char GMapArea::XOR_BORDER_TAG[] = "xor"; const char GMapArea::SOLID_BORDER_TAG[] = "border"; const char GMapArea::SHADOW_IN_BORDER_TAG[] = "shadow_in"; const char GMapArea::SHADOW_OUT_BORDER_TAG[] = "shadow_out"; const char GMapArea::SHADOW_EIN_BORDER_TAG[] = "shadow_ein"; const char GMapArea::SHADOW_EOUT_BORDER_TAG[] = "shadow_eout"; const char GMapArea::BORDER_AVIS_TAG[] = "border_avis"; const char GMapArea::HILITE_TAG[] = "hilite"; const char GMapArea::URL_TAG[] = "url"; const char GMapArea::TARGET_SELF[] = "_self"; static const char zero_width[] = ERR_MSG("GMapAreas.zero_width"); static const char zero_height[] = ERR_MSG("GMapAreas.zero_height"); static const char width_1[] = ERR_MSG("GMapAreas.width_1"); static const char width_3_32 [] = ERR_MSG("GMapAreas.width_3-32"); static const char error_poly_border [] = ERR_MSG("GMapAreas.poly_border"); static const char error_poly_hilite [] = ERR_MSG("GMapAreas.poly_hilite"); static const char error_oval_border [] = ERR_MSG("GMapAreas.oval_border"); static const char error_oval_hilite [] = ERR_MSG("GMapAreas.oval_hilite"); static const char error_too_few_points [] = ERR_MSG("GMapAreas.too_few_points"); static const char error_intersect [] = ERR_MSG("GMapAreas.intersect"); GMapArea::~GMapArea() {} GMapRect::~GMapRect() {} GMapPoly::~GMapPoly() {} GMapOval::~GMapOval() {} void GMapArea::initialize_bounds(void) { xmin=gma_get_xmin(); xmax=gma_get_xmax(); ymin=gma_get_ymin(); ymax=gma_get_ymax(); bounds_initialized=true; } int GMapArea::get_xmin(void) const { if (!bounds_initialized) const_cast(this)->initialize_bounds(); return xmin; } int GMapArea::get_ymin(void) const { if (!bounds_initialized) const_cast(this)->initialize_bounds(); return ymin; } int GMapArea::get_xmax(void) const { if (!bounds_initialized) const_cast(this)->initialize_bounds(); return xmax; } int GMapArea::get_ymax(void) const { if (!bounds_initialized) const_cast(this)->initialize_bounds(); return ymax; } GRect GMapArea::get_bound_rect(void) const { return GRect(get_xmin(), get_ymin(), get_xmax()-get_xmin(), get_ymax()-get_ymin()); } void GMapArea::move(int dx, int dy) { if (dx || dy) { if (bounds_initialized) { xmin+=dx; ymin+=dy; xmax+=dx; ymax+=dy; } gma_move(dx, dy); } } void GMapArea::resize(int new_width, int new_height) { if (get_xmax()-get_xmin()!=new_width || get_ymax()-get_ymin()!=new_height) { gma_resize(new_width, new_height); bounds_initialized=false; } } void GMapArea::transform(const GRect & grect) { if (grect.xmin!=get_xmin() || grect.ymin!=get_ymin() || grect.xmax!=get_xmax() || grect.ymax!=get_ymax()) { gma_transform(grect); bounds_initialized=false; } } char const * const GMapArea::check_object(void) { char const *retval; if (get_xmax()==get_xmin()) { retval=zero_width; } else if (get_ymax()==get_ymin()) { retval=zero_height; } else if ((border_type==XOR_BORDER || border_type==SOLID_BORDER) && border_width!=1) { retval=width_1; } else if ((border_type==SHADOW_IN_BORDER || border_type==SHADOW_OUT_BORDER || border_type==SHADOW_EIN_BORDER || border_type==SHADOW_EOUT_BORDER)&& (border_width<3 || border_width>32)) { retval=width_3_32; }else { retval=gma_check_object(); } return retval; } bool GMapArea::is_point_inside(int x, int y) const { if (!bounds_initialized) const_cast(this)->initialize_bounds(); return (x>=xmin && x=ymin && y> 16, (border_color & 0xff00) >> 8, (border_color & 0xff)); static const GUTF8String left('('); static const GUTF8String right(')'); static const GUTF8String space(' '); static const GUTF8String quote('"'); GUTF8String border_type_str; switch(border_type) { case NO_BORDER: border_type_str=left+NO_BORDER_TAG+right; break; case XOR_BORDER: border_type_str=left+XOR_BORDER_TAG+right; break; case SOLID_BORDER: border_type_str=left+SOLID_BORDER_TAG+space+border_color_str+right; break; case SHADOW_IN_BORDER: border_type_str=left+SHADOW_IN_BORDER_TAG+space+GUTF8String(border_width)+right; break; case SHADOW_OUT_BORDER: border_type_str=left+SHADOW_OUT_BORDER_TAG+space+GUTF8String(border_width)+right; break; case SHADOW_EIN_BORDER: border_type_str=left+SHADOW_EIN_BORDER_TAG+space+GUTF8String(border_width)+right; break; case SHADOW_EOUT_BORDER: border_type_str=left+SHADOW_EOUT_BORDER_TAG+space+GUTF8String(border_width)+right; break; default: border_type_str=left+XOR_BORDER_TAG+right; break; } GUTF8String hilite_str; if (hilite_color!=0xffffffff) { hilite_str.format("(%s #%02X%02X%02X)", HILITE_TAG, (hilite_color & 0xff0000) >> 16, (hilite_color & 0xff00) >> 8, (hilite_color & 0xff)); } GUTF8String URL; if (target1==TARGET_SELF) { URL=quote+url1+quote; }else { URL=left+URL_TAG+space+quote+url1+quote+space+quote+target1+quote+right; } GUTF8String total=left+MAPAREA_TAG+space+URL+space+quote+comment1+quote+space+gma_print()+border_type_str; if (border_always_visible) total+=space+left+BORDER_AVIS_TAG+right; if ( hilite_str.length() > 0 ) total+=space+hilite_str; total+=right; return total; } /* void GMapArea::map(GRectMapper &mapper) { get_bound_rect(); GRect rect = GRect(xmin, ymin, xmax, ymax); mapper.map(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); } void GMapArea::unmap(GRectMapper &mapper) { get_bound_rect(); GRect rect = GRect(xmin, ymin, xmax, ymax); mapper.unmap(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); } */ /// Virtual function generating a list of defining coordinates /// (default are the opposite corners of the enclosing rectangle) void GMapArea::get_coords( GList & CoordList ) const { CoordList.append( get_xmin() ); CoordList.append( get_ymin() ); CoordList.append( get_xmax() ); CoordList.append( get_ymax() ); } /**************************************************************************** **************************** GMapRect definition **************************** ****************************************************************************/ void GMapRect::gma_resize(int new_width, int new_height) { xmax=xmin+new_width; ymax=ymin+new_height; } void GMapRect::gma_transform(const GRect & grect) { xmin=grect.xmin; ymin=grect.ymin; xmax=grect.xmax; ymax=grect.ymax; } GUTF8String GMapRect::gma_print(void) { GUTF8String buffer; return buffer.format("(%s %d %d %d %d) ", RECT_TAG, xmin, ymin, xmax-xmin, ymax-ymin); } void GMapRect::map(GRectMapper &mapper) { get_bound_rect(); GRect rect; rect.xmin = xmin; rect.xmax = xmax; rect.ymin = ymin; rect.ymax = ymax; mapper.map(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); } void GMapRect::unmap(GRectMapper &mapper) { get_bound_rect(); GRect rect; rect.xmin = xmin; rect.xmax = xmax; rect.ymin = ymin; rect.ymax = ymax; mapper.unmap(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); } /**************************************************************************** **************************** GMapPoly definition **************************** ****************************************************************************/ inline int GMapPoly::sign(int x) { return x<0 ? -1 : x>0 ? 1 : 0; } bool GMapPoly::does_side_cross_rect(const GRect & grect, int side) { int x1=xx[side], x2=xx[(side+1)%points]; int y1=yy[side], y2=yy[(side+1)%points]; int xmin=x1grect.xmax || ymaxgrect.ymax) return false; return x1>=grect.xmin && x1<=grect.xmax && y1>=grect.ymin && y1<=grect.ymax || x2>=grect.xmin && x2<=grect.xmax && y2>=grect.ymin && y2<=grect.ymax || do_segments_intersect(grect.xmin, grect.ymin, grect.xmax, grect.ymax, x1, y1, x2, y2) || do_segments_intersect(grect.xmax, grect.ymin, grect.xmin, grect.ymax, x1, y1, x2, y2); } bool GMapPoly::is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2) { int res1=(x-x1)*(x2-x1)+(y-y1)*(y2-y1); int res2=(x-x2)*(x2-x1)+(y-y2)*(y2-y1); return sign(res1)*sign(res2)<=0; } bool GMapPoly::do_segments_intersect(int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22) { int res11=(x11-x21)*(y22-y21)-(y11-y21)*(x22-x21); int res12=(x12-x21)*(y22-y21)-(y12-y21)*(x22-x21); int res21=(x21-x11)*(y12-y11)-(y21-y11)*(x12-x11); int res22=(x22-x11)*(y12-y11)-(y22-y11)*(x12-x11); if (!res11 && !res12) { // Segments are on the same line return is_projection_on_segment(x11, y11, x21, y21, x22, y22) || is_projection_on_segment(x12, y12, x21, y21, x22, y22) || is_projection_on_segment(x21, y21, x11, y11, x12, y12) || is_projection_on_segment(x22, y22, x11, y11, x12, y12); } int sign1=sign(res11)*sign(res12); int sign2=sign(res21)*sign(res22); return sign1<=0 && sign2<=0; } bool GMapPoly::are_segments_parallel(int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22) { return (x12-x11)*(y22-y21)-(y12-y11)*(x22-x21)==0; } char const * const GMapPoly::check_data(void) { if (open && points<2 || !open && points<3) return error_too_few_points; for(int i=0;i0 || res1>0 && res2<0) { int x1=xx[i%points], y1=yy[i%points]; int x2=xx[(i+1)%points], y2=yy[(i+1)%points]; int _res1=(xin-x1)*(y2-y1)-(yin-y1)*(x2-x1); int _res2=(xfar-x1)*(y2-y1)-(yin-y1)*(x2-x1); if (!_res1 || !_res2) { // The point is on this boundary return true; } if (sign(_res1)*sign(_res2)<0) intersections++; } } return (intersections % 2)!=0; } int GMapPoly::gma_get_xmin(void) const { int x=xx[0]; for(int i=1;ixx[i]) x=xx[i]; return x; } int GMapPoly::gma_get_xmax(void) const { int x=xx[0]; for(int i=1;iyy[i]) y=yy[i]; return y; } int GMapPoly::gma_get_ymax(void) const { int y=yy[0]; for(int i=1;i & CoordList ) const { for(int i = 0 ; i < points ; i++) { CoordList.append( xx[i] ); CoordList.append( yy[i] ); } } void GMapPoly::map(GRectMapper &mapper) { get_bound_rect(); for(int i=0; ib) { rmin=b; rmax=a; f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); xf1=xc+f; xf2=xc-f; yf1=yf2=yc; } else { rmin=a; rmax=b; f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); yf1=yc+f; yf2=yc-f; xf1=xf2=xc; } } GMapOval::GMapOval(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), xmax(rect.xmax), ymax(rect.ymax) { initialize(); } GUTF8String GMapOval::gma_print(void) { GUTF8String buffer; return buffer.format("(%s %d %d %d %d) ", OVAL_TAG, xmin, ymin, xmax-xmin, ymax-ymin); } void GMapOval::map(GRectMapper &mapper) { get_bound_rect(); GRect rect; rect.xmin = xmin; rect.xmax = xmax; rect.ymin = ymin; rect.ymax = ymax; mapper.map(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); initialize(); } void GMapOval::unmap(GRectMapper &mapper) { get_bound_rect(); GRect rect; rect.xmin = xmin; rect.xmax = xmax; rect.ymin = ymin; rect.ymax = ymax; mapper.unmap(rect); xmin = rect.xmin; ymin = rect.ymin; xmax = rect.xmax; ymax = rect.ymax; clear_bounds(); initialize(); } GMapArea::GMapArea(void) : target("_self"), border_type(NO_BORDER), border_always_visible(false), border_color(0xff), border_width(1), hilite_color(0xffffffff), bounds_initialized(0) {} GMapRect::GMapRect(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} GMapRect::GMapRect(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), xmax(rect.xmax), ymax(rect.ymax) {} GMapRect & GMapRect::operator=(const GRect & rect) { xmin=rect.xmin; xmax=rect.xmax; ymin=rect.ymin; ymax=rect.ymax; return *this; } void GMapRect::gma_move(int dx, int dy) { xmin+=dx; xmax+=dx; ymin+=dy; ymax+=dy; } bool GMapRect::gma_is_point_inside(const int x, const int y) const { return (x>=xmin)&&(x=ymin)&&(y GMapRect::get_copy(void) const { return new GMapRect(*this); } GMapPoly::GMapPoly(void) : points(0), sides(0) {} void GMapPoly::move_vertex(int i, int x, int y) { xx[i]=x; yy[i]=y; clear_bounds(); } GP GMapPoly::get_copy(void) const { return new GMapPoly(*this); } GMapOval::GMapOval(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} void GMapOval::gma_move(int dx, int dy) { xmin+=dx; xmax+=dx; ymin+=dy; ymax+=dy; xf1+=dx; yf1+=dy; xf2+=dx; yf2+=dy; } GP GMapOval::get_copy(void) const { return new GMapOval(*this); } static GUTF8String GMapArea2xmltag(const GMapArea &area,const GUTF8String &coords) { GUTF8String retval("\n"; } GUTF8String GMapRect::get_xmltag(const int height) const { return GMapArea2xmltag( *this, GUTF8String(get_xmin()) +","+GUTF8String(height-1-get_ymax()) +","+GUTF8String(get_xmax()) +","+GUTF8String(height-1-get_ymin())); #if 0 GUTF8String retval; return retval; #endif } GUTF8String GMapOval::get_xmltag(const int height) const { return GMapArea2xmltag( *this, GUTF8String(get_xmin()) +","+GUTF8String(height-1-get_ymax()) +","+GUTF8String(get_xmax()) +","+GUTF8String(height-1-get_ymin())); #if 0 GUTF8String retval; return retval; #endif } GUTF8String GMapPoly::get_xmltag(const int height) const { GList CoordList; get_coords(CoordList); GPosition pos=CoordList; GUTF8String retval; if(pos) { GUTF8String coords(CoordList[pos]); while(++pos) { coords+=","+GUTF8String(height-1-CoordList[pos]); if(! ++pos) break; coords+=","+GUTF8String(CoordList[pos]); } retval=GMapArea2xmltag( *this, coords); } return retval; } #ifdef HAVE_NAMESPACES } # ifndef NOT_USING_DJVU_NAMESPACE using namespace DJVU; # endif #endif