Blob Blame History Raw
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/

/* This header contains code specific to a pict mac file
 */
#include <string.h>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>

#include <librevenge/librevenge.h>

#include "libmwaw_internal.hxx"

#include "MWAWGraphicEncoder.hxx"
#include "MWAWGraphicStyle.hxx"

#include "MWAWGraphicShape.hxx"

////////////////////////////////////////////////////////////
// MWAWGraphicShape::PathData
////////////////////////////////////////////////////////////
std::ostream &operator<<(std::ostream &o, MWAWGraphicShape::PathData const &path)
{
  o << path.m_type;
  switch (path.m_type) {
  case 'H':
    o << ":" << path.m_x[0];
    break;
  case 'V':
    o << ":" << path.m_x[1];
    break;
  case 'M':
  case 'L':
  case 'T':
    o << ":" << path.m_x;
    break;
  case 'Q':
  case 'S':
    o << ":" << path.m_x << ":" << path.m_x1;
    break;
  case 'C':
    o << ":" << path.m_x << ":" << path.m_x1 << ":" << path.m_x2;
    break;
  case 'A':
    o << ":" << path.m_x << ":r=" << path.m_r;
    if (path.m_largeAngle) o << ":largeAngle";
    if (path.m_sweep) o << ":sweep";
    if (path.m_rotate<0 || path.m_rotate>0) o << ":rot=" << path.m_rotate;
  case 'Z':
    break;
  default:
    o << "###";
  }
  return o;
}

void MWAWGraphicShape::PathData::translate(MWAWVec2f const &decal)
{
  if (m_type=='Z')
    return;
  m_x += decal;
  if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
    return;
  m_x1 += decal;
  if (m_type=='Q' || m_type=='S')
    return;
  m_x2 += decal;
}

void MWAWGraphicShape::PathData::scale(MWAWVec2f const &scaling)
{
  if (m_type=='Z')
    return;
  m_x = MWAWVec2f(m_x[0]*scaling[0], m_x[1]*scaling[1]);
  if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
    return;
  m_x1 = MWAWVec2f(m_x1[0]*scaling[0], m_x1[1]*scaling[1]);
  if (m_type=='Q' || m_type=='S')
    return;
  m_x2 = MWAWVec2f(m_x2[0]*scaling[0], m_x2[1]*scaling[1]);
}

void MWAWGraphicShape::PathData::rotate(float angle, MWAWVec2f const &decal)
{
  if (m_type=='Z')
    return;
  float angl=angle*float(M_PI/180.);
  m_x = MWAWVec2f(std::cos(angl)*m_x[0]-std::sin(angl)*m_x[1],
                  std::sin(angl)*m_x[0]+std::cos(angl)*m_x[1])+decal;
  if (m_type=='A') {
    m_rotate += angle;
    return;
  }
  if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
    return;
  m_x1 = MWAWVec2f(std::cos(angl)*m_x1[0]-std::sin(angl)*m_x1[1],
                   std::sin(angl)*m_x1[0]+std::cos(angl)*m_x1[1])+decal;
  if (m_type=='Q' || m_type=='S')
    return;
  m_x2 = MWAWVec2f(std::cos(angl)*m_x2[0]-std::sin(angl)*m_x2[1],
                   std::sin(angl)*m_x2[0]+std::cos(angl)*m_x2[1])+decal;
}

void MWAWGraphicShape::PathData::transform(MWAWTransformation const &matrix, float rotation)
{
  if (m_type=='Z')
    return;
  m_x = matrix*m_x;
  if (m_type=='A') {
    m_rotate += rotation;
    return;
  }
  if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
    return;
  m_x1 = matrix*m_x1;
  if (m_type=='Q' || m_type=='S')
    return;
  m_x2 = matrix*m_x2;
}

bool MWAWGraphicShape::PathData::get(librevenge::RVNGPropertyList &list, MWAWVec2f const &orig) const
{
  list.clear();
  std::string type("");
  type += m_type;
  list.insert("librevenge:path-action", type.c_str());
  if (m_type=='Z')
    return true;
  if (m_type=='H') {
    list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
    return true;
  }
  if (m_type=='V') {
    list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
    return true;
  }
  list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
  list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
  if (m_type=='M' || m_type=='L' || m_type=='T')
    return true;
  if (m_type=='A') {
    list.insert("svg:rx",double(m_r[0]), librevenge::RVNG_POINT);
    list.insert("svg:ry",double(m_r[1]), librevenge::RVNG_POINT);
    list.insert("librevenge:large-arc", m_largeAngle);
    list.insert("librevenge:sweep", m_sweep);
    list.insert("librevenge:rotate", double(m_rotate), librevenge::RVNG_GENERIC);
    return true;
  }
  list.insert("svg:x1",double(m_x1[0]-orig[0]), librevenge::RVNG_POINT);
  list.insert("svg:y1",double(m_x1[1]-orig[1]), librevenge::RVNG_POINT);
  if (m_type=='Q' || m_type=='S')
    return true;
  list.insert("svg:x2",double(m_x2[0]-orig[0]), librevenge::RVNG_POINT);
  list.insert("svg:y2",double(m_x2[1]-orig[1]), librevenge::RVNG_POINT);
  if (m_type=='C')
    return true;
  MWAW_DEBUG_MSG(("MWAWGraphicShape::PathData::get: unknown command %c\n", m_type));
  list.clear();
  return false;
}

int MWAWGraphicShape::PathData::cmp(MWAWGraphicShape::PathData const &a) const
{
  if (m_type < a.m_type) return 1;
  if (m_type > a.m_type) return 1;
  int diff = m_x.cmp(a.m_x);
  if (diff) return diff;
  diff = m_x1.cmp(a.m_x1);
  if (diff) return diff;
  diff = m_x2.cmp(a.m_x2);
  if (diff) return diff;
  diff = m_r.cmp(a.m_r);
  if (diff) return diff;
  if (m_rotate < a.m_rotate) return 1;
  if (m_rotate > a.m_rotate) return -1;
  if (m_largeAngle != a.m_largeAngle)
    return m_largeAngle ? 1 : -1;
  if (m_sweep != a.m_sweep)
    return m_sweep ? 1 : -1;
  return 0;
}

////////////////////////////////////////////////////////////
// MWAWGraphicShape
////////////////////////////////////////////////////////////
MWAWGraphicShape::~MWAWGraphicShape()
{
}

MWAWGraphicShape MWAWGraphicShape::line(MWAWVec2f const &orig, MWAWVec2f const &dest)
{
  MWAWGraphicShape res;
  res.m_type = MWAWGraphicShape::Line;
  res.m_vertices.resize(2);
  res.m_vertices[0]=orig;
  res.m_vertices[1]=dest;
  MWAWVec2f minPt(orig), maxPt(orig);
  for (int c=0; c<2; ++c) {
    if (orig[c] < dest[c])
      maxPt[c]=dest[c];
    else
      minPt[c]=dest[c];
  }
  res.m_bdBox=MWAWBox2f(minPt,maxPt);
  return res;
}

MWAWGraphicShape MWAWGraphicShape::measure(MWAWVec2f const &orig, MWAWVec2f const &dest)
{
  MWAWGraphicShape res=line(orig,dest);
  res.m_type= MWAWGraphicShape::Measure;
  return res;
}

std::ostream &operator<<(std::ostream &o, MWAWGraphicShape const &sh)
{
  o << "box=" << sh.m_bdBox << ",";
  switch (sh.m_type) {
  case MWAWGraphicShape::Line:
    o << "line,";
    if (sh.m_vertices.size()!=2)
      o << "###pts,";
    else
      o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
    break;
  case MWAWGraphicShape::Measure:
    o << "measure,";
    if (sh.m_vertices.size()!=2)
      o << "###pts,";
    else
      o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
    break;
  case MWAWGraphicShape::Rectangle:
    o << "rect,";
    if (sh.m_formBox!=sh.m_bdBox)
      o << "box[rect]=" << sh.m_formBox << ",";
    if (sh.m_cornerWidth!=MWAWVec2f(0,0))
      o << "corners=" << sh.m_cornerWidth << ",";
    break;
  case MWAWGraphicShape::Circle:
    o << "circle,";
    break;
  case MWAWGraphicShape::Arc:
  case MWAWGraphicShape::Pie:
    o << (sh.m_type == MWAWGraphicShape::Arc ? "arc," : "pie,");
    o << "box[ellipse]=" << sh.m_formBox << ",";
    o << "angle=" << sh.m_arcAngles << ",";
    break;
  case MWAWGraphicShape::Polygon:
  case MWAWGraphicShape::Polyline:
    if (sh.m_type==MWAWGraphicShape::Polygon)
      o << "polygon,pts=[";
    else
      o << "polyline,pts=[";
    for (auto const &pt : sh.m_vertices)
      o << pt << ",";
    o << "],";
    break;
  case MWAWGraphicShape::Path:
    o << "path,pts=[";
    for (auto const &pt : sh.m_path)
      o << pt << ",";
    o << "],";
    break;
  case MWAWGraphicShape::ShapeUnknown:
#if !defined(__clang__)
  default:
#endif
    o << "###unknown[shape],";
    break;
  }
  o << sh.m_extra;
  return o;
}

int MWAWGraphicShape::cmp(MWAWGraphicShape const &a) const
{
  if (m_type < a.m_type) return 1;
  if (m_type > a.m_type) return -1;
  if (m_bdBox < a.m_bdBox) return 1;
  if (m_bdBox > a.m_bdBox) return -1;
  if (m_formBox < a.m_formBox) return 1;
  if (m_formBox > a.m_formBox) return -1;
  int diff = m_cornerWidth.cmp(a.m_cornerWidth);
  if (diff) return diff;
  diff = m_arcAngles.cmp(a.m_arcAngles);
  if (diff) return diff;
  if (m_vertices.size()<a.m_vertices.size()) return 1;
  if (m_vertices.size()>a.m_vertices.size()) return -1;
  for (size_t pt=0; pt < m_vertices.size(); ++pt) {
    diff = m_vertices[pt].cmp(a.m_vertices[pt]);
    if (diff) return diff;
  }
  if (m_path.size()<a.m_path.size()) return 1;
  if (m_path.size()>a.m_path.size()) return -1;
  for (size_t pt=0; pt < m_path.size(); ++pt) {
    diff = m_path[pt].cmp(a.m_path[pt]);
    if (diff) return diff;
  }
  return 0;
}

MWAWBox2f MWAWGraphicShape::getBdBox(MWAWGraphicStyle const &style, bool moveToO) const
{
  MWAWBox2f bdBox=m_bdBox;
  if (moveToO)
    bdBox=MWAWBox2f(MWAWVec2f(0,0),m_bdBox.size());
  if (style.hasLine())
    bdBox.extend(style.m_lineWidth/2.f);
  if (m_type==Line) {
    // fixme: add 4pt for each arrows
    int numArrows=(style.m_arrows[0].isEmpty() ? 0 : 1)+(style.m_arrows[1].isEmpty() ? 0 : 1);
    if (numArrows) bdBox.extend(float(2*numArrows));
  }
  return bdBox;
}

void MWAWGraphicShape::translate(MWAWVec2f const &decal)
{
  if (decal==MWAWVec2f(0,0))
    return;
  m_bdBox=MWAWBox2f(m_bdBox.min()+decal, m_bdBox.max()+decal);
  m_formBox=MWAWBox2f(m_formBox.min()+decal, m_formBox.max()+decal);
  for (auto &pt : m_vertices)
    pt+=decal;
  for (auto &pt : m_path)
    pt.translate(decal);
}

void MWAWGraphicShape::scale(MWAWVec2f const &scaling)
{
  // checkme: does not work for symetry if shape is an arc...
  m_bdBox=MWAWBox2f(MWAWVec2f(scaling[0]*m_bdBox.min()[0],scaling[1]*m_bdBox.min()[1]),
                    MWAWVec2f(scaling[0]*m_bdBox.max()[0],scaling[1]*m_bdBox.max()[1]));
  m_formBox=MWAWBox2f(MWAWVec2f(scaling[0]*m_formBox.min()[0],scaling[1]*m_formBox.min()[1]),
                      MWAWVec2f(scaling[0]*m_formBox.max()[0],scaling[1]*m_formBox.max()[1]));
  for (auto &pt : m_vertices)
    pt=MWAWVec2f(scaling[0]*pt[0], scaling[1]*pt[1]);
  for (auto &pt : m_path)
    pt.scale(scaling);
}

MWAWGraphicShape MWAWGraphicShape::rotate(float angle, MWAWVec2f const &center) const
{
  while (angle >= 360) angle -= 360;
  while (angle <= -360) angle += 360;
  if (angle >= -1.e-3f && angle <= 1.e-3f) return *this;
  float angl=angle*float(M_PI/180.);
  MWAWVec2f decal=center-MWAWVec2f(std::cos(angl)*center[0]-std::sin(angl)*center[1],
                                   std::sin(angl)*center[0]+std::cos(angl)*center[1]);
  MWAWBox2f fBox;
  for (int i=0; i < 4; ++i) {
    MWAWVec2f pt=MWAWVec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
    pt = MWAWVec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
                   std::sin(angl)*pt[0]+std::cos(angl)*pt[1])+decal;
    if (i==0) fBox=MWAWBox2f(pt,pt);
    else fBox=fBox.getUnion(MWAWBox2f(pt,pt));
  }
  MWAWGraphicShape res = path(fBox);
  res.m_path=getPath(false);
  for (auto &pt : res.m_path)
    pt.rotate(angle, decal);
  return res;
}

MWAWGraphicShape MWAWGraphicShape::transform(MWAWTransformation const &matrix) const
{
  if (matrix.isIdentity()) return *this;
  if (matrix[0][1]<=0 && matrix[0][1]>=0 && matrix[1][0]<=0 && matrix[1][0]>=0) {
    MWAWGraphicShape res=*this;
    if (matrix[0][0]<1 || matrix[0][0]>1 || matrix[1][1]<1 || matrix[1][1]>1)
      res.scale(MWAWVec2f(matrix[0][0], matrix[1][1]));
    res.translate(MWAWVec2f(matrix[0][2],matrix[1][2]));
    return res;
  }

  MWAWBox2f fBox;
  for (int i=0; i < 4; ++i) {
    MWAWVec2f pt = matrix*MWAWVec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
    if (i==0) fBox=MWAWBox2f(pt,pt);
    else fBox=fBox.getUnion(MWAWBox2f(pt,pt));
  }
  MWAWGraphicShape res = path(fBox);
  res.m_path=getPath(true);

  MWAWTransformation transf;
  float rotation=0;
  MWAWVec2f shearing;
  if (!matrix.decompose(rotation,shearing,transf,fBox.center()))
    rotation=0;
  for (auto &pt : res.m_path)
    pt.transform(matrix, rotation);
  return res;
}

bool MWAWGraphicShape::addPathTo(MWAWVec2f const &orig, librevenge::RVNGPropertyListVector &vect) const
{
  MWAWVec2f decal=orig-m_bdBox[0];
  std::vector<MWAWGraphicShape::PathData> fPath=getPath(false);
  size_t n=fPath.size();
  if (!n) {
    MWAW_DEBUG_MSG(("MWAWGraphicShape::addPathTo: can not find the path\n"));
    return false;
  }
  librevenge::RVNGPropertyList list;
  for (auto const &pt : fPath) {
    list.clear();
    if (pt.get(list, -1.0f*decal))
      vect.append(list);
  }
  if (fPath[n-1].m_type != 'Z') {
    // odg need a closed path to draw surface, so ...
    list.clear();
    list.insert("librevenge:path-action", "Z");
    vect.append(list);
  }
  return true;
}

MWAWGraphicShape::Command MWAWGraphicShape::addTo(MWAWVec2f const &orig, bool asSurface, librevenge::RVNGPropertyList &propList) const
{
  MWAWVec2f pt;
  librevenge::RVNGPropertyList list;
  librevenge::RVNGPropertyListVector vect;
  MWAWVec2f decal=orig-m_bdBox[0];
  switch (m_type) {
  case Line:
  case Measure:
    if (m_vertices.size()!=2) break;
    if (m_type==Measure)
      propList.insert("draw:show-unit", true);
    pt=m_vertices[0]+decal;
    list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
    list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
    vect.append(list);
    pt=m_vertices[1]+decal;
    list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
    list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
    vect.append(list);
    propList.insert("svg:points", vect);
    return C_Polyline;
  case Rectangle:
    if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0) {
      propList.insert("svg:rx",double(m_cornerWidth[0]), librevenge::RVNG_POINT);
      propList.insert("svg:ry",double(m_cornerWidth[1]), librevenge::RVNG_POINT);
    }
    pt=m_formBox[0]+decal;
    propList.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
    propList.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
    pt=m_formBox.size();
    propList.insert("svg:width",double(pt.x()), librevenge::RVNG_POINT);
    propList.insert("svg:height",double(pt.y()), librevenge::RVNG_POINT);
    return C_Rectangle;
  case Circle:
    pt=0.5f*(m_formBox[0]+m_formBox[1])+decal;
    propList.insert("svg:cx",double(pt.x()), librevenge::RVNG_POINT);
    propList.insert("svg:cy",double(pt.y()), librevenge::RVNG_POINT);
    pt=0.5f*(m_formBox[1]-m_formBox[0]);
    propList.insert("svg:rx",double(pt.x()), librevenge::RVNG_POINT);
    propList.insert("svg:ry",double(pt.y()), librevenge::RVNG_POINT);
    return C_Ellipse;
  case Arc:
  case Pie: {
    MWAWVec2f center=0.5f*(m_formBox[0]+m_formBox[1])+decal;
    MWAWVec2f rad=0.5f*(m_formBox[1]-m_formBox[0]);
    float angl0=m_arcAngles[0];
    float angl1=m_arcAngles[1];
    if (rad[1]<0) {
      static bool first=true;
      if (first) {
        MWAW_DEBUG_MSG(("MWAWGraphicShape::addTo: oops radiusY for arc is negative, inverse it\n"));
        first=false;
      }
      rad[1]=-rad[1];
    }
    while (angl1<angl0)
      angl1+=360.f;
    while (angl1>angl0+360.f)
      angl1-=360.f;
    if (angl1-angl0>=180.f && angl1-angl0<=180.f)
      angl1+=0.01f;
    float angl=angl0*float(M_PI/180.);
    bool addCenter=m_type==Pie && asSurface;
    if (addCenter) {
      pt=center;
      list.insert("librevenge:path-action", "M");
      list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
      list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
      vect.append(list);
    }
    list.clear();
    pt=center+MWAWVec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
    list.insert("librevenge:path-action", addCenter ? "L" : "M");
    list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
    list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
    vect.append(list);

    list.clear();
    angl=angl1*float(M_PI/180.);
    pt=center+MWAWVec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
    list.insert("librevenge:path-action", "A");
    list.insert("librevenge:large-arc", !(angl1-angl0<180.f));
    list.insert("librevenge:sweep", false);
    list.insert("svg:rx",double(rad.x()), librevenge::RVNG_POINT);
    list.insert("svg:ry",double(rad.y()), librevenge::RVNG_POINT);
    list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
    list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
    vect.append(list);
    if (asSurface) {
      list.clear();
      list.insert("librevenge:path-action", "Z");
      vect.append(list);
    }

    propList.insert("svg:d", vect);
    return C_Path;
  }
  case Polygon:
  case Polyline: {
    size_t n=m_vertices.size();
    if (n<2) break;
    for (auto point : m_vertices) {
      list.clear();
      point += decal;
      list.insert("svg:x", double(point.x()), librevenge::RVNG_POINT);
      list.insert("svg:y", double(point.y()), librevenge::RVNG_POINT);
      vect.append(list);
    }
    propList.insert("svg:points", vect);
    return (asSurface && m_type==Polygon) ? C_Polygon : C_Polyline;
  }
  case Path: {
    size_t n=m_path.size();
    if (!n) break;
    for (auto const &point : m_path) {
      list.clear();
      if (point.get(list, -1.0f*decal))
        vect.append(list);
    }
    if (asSurface && m_path[n-1].m_type != 'Z') {
      // odg need a closed path to draw surface, so ...
      list.clear();
      list.insert("librevenge:path-action", "Z");
      vect.append(list);
    }
    propList.insert("svg:d", vect);
    return C_Path;
  }
  case ShapeUnknown:
#if !defined(__clang__)
  default:
#endif
    break;
  }
  MWAW_DEBUG_MSG(("MWAWGraphicShape::addTo: can not send a shape with type=%d\n", int(m_type)));
  return C_Bad;
}

std::vector<MWAWGraphicShape::PathData> MWAWGraphicShape::getPath(bool forTransformation) const
{
  std::vector<MWAWGraphicShape::PathData> res;
  float const delta=0.55228f;
  switch (m_type) {
  case Measure:
    MWAW_DEBUG_MSG(("MWAWGraphicShape::getPath: called on a measure, transform it in line\n"));
    MWAW_FALLTHROUGH;
  case Line:
  case Polygon:
  case Polyline: {
    size_t n=m_vertices.size();
    if (n<2) break;
    res.push_back(PathData('M',m_vertices[0]));
    for (size_t i = 1; i < n; ++i)
      res.push_back(PathData('L', m_vertices[i]));
    break;
  }
  case Rectangle:
    if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0) {
      MWAWBox2f box=m_formBox;
      if (box.min()[0]>box.max()[0]) std::swap(box.min()[0],box.max()[0]);
      if (box.min()[1]>box.max()[1]) std::swap(box.min()[1],box.max()[1]);
      MWAWVec2f c=m_cornerWidth;
      if (2*c[0]>box.size()[0]) c[0]=0.5f*box.size()[0];
      if (2*c[1]>box.size()[1]) c[1]=0.5f*box.size()[1];
      if (forTransformation) {
        MWAWVec2f pt0(box[1][0]-c[0],box[0][1]);
        res.push_back(PathData('M',pt0));
        MWAWVec2f pt1(box[1][0],box[0][1]+c[1]);
        res.push_back(PathData('C',pt1,pt0+MWAWVec2f(delta*c[0],0),pt1-MWAWVec2f(0,delta*c[1])));
        pt0=MWAWVec2f(box[1][0],box[1][1]-c[1]);
        res.push_back(PathData('L',pt0));
        pt1=MWAWVec2f(box[1][0]-c[0],box[1][1]);
        res.push_back(PathData('C',pt1,pt0+MWAWVec2f(0,delta*c[1]),pt1+MWAWVec2f(delta*c[0],0)));
        pt0=MWAWVec2f(box[0][0]+c[0],box[1][1]);
        res.push_back(PathData('L',pt0));
        pt1=MWAWVec2f(box[0][0],box[1][1]-c[1]);
        res.push_back(PathData('C',pt1,pt0-MWAWVec2f(delta*c[0],0),pt1+MWAWVec2f(0,delta*c[1])));
        pt0=MWAWVec2f(box[0][0],box[0][1]+c[1]);
        res.push_back(PathData('L',pt0));
        pt1=MWAWVec2f(box[0][0]+c[0],box[0][1]);
        res.push_back(PathData('C',pt1,pt0-MWAWVec2f(0,delta*c[1]),pt1-MWAWVec2f(delta*c[0],0)));
      }
      else {
        res.push_back(PathData('M',MWAWVec2f(box[1][0]-c[0],box[0][1])));
        PathData data('A',MWAWVec2f(box[1][0],box[0][1]+c[1]));
        data.m_r=c;
        data.m_sweep=true;
        res.push_back(data);
        res.push_back(PathData('L',MWAWVec2f(box[1][0],box[1][1]-c[1])));
        data.m_x=MWAWVec2f(box[1][0]-c[0],box[1][1]);
        res.push_back(data);
        res.push_back(PathData('L',MWAWVec2f(box[0][0]+c[0],box[1][1])));
        data.m_x=MWAWVec2f(box[0][0],box[1][1]-c[1]);
        res.push_back(data);
        res.push_back(PathData('L',MWAWVec2f(box[0][0],box[0][1]+c[1])));
        data.m_x=MWAWVec2f(box[0][0]+c[0],box[0][1]);
        res.push_back(data);
      }
      res.push_back(PathData('Z'));
      break;
    }
    res.push_back(PathData('M',m_formBox[0]));
    res.push_back(PathData('L',MWAWVec2f(m_formBox[0][0],m_formBox[1][1])));
    res.push_back(PathData('L',m_formBox[1]));
    res.push_back(PathData('L',MWAWVec2f(m_formBox[1][0],m_formBox[0][1])));
    res.push_back(PathData('Z'));
    break;
  case Circle: {
    if (forTransformation) {
      MWAWVec2f center=m_formBox.center();
      MWAWVec2f dir=0.5f*delta*(m_formBox[1]-m_formBox[0]);
      MWAWVec2f pt0(m_formBox[0][0],center[1]);
      res.push_back(PathData('M',pt0));
      MWAWVec2f pt1(center[0],m_formBox[0][1]);
      res.push_back(PathData('C',pt1, pt0-MWAWVec2f(0,dir[1]), pt1-MWAWVec2f(dir[0],0)));
      pt0=MWAWVec2f(m_formBox[1][0],center[1]);
      res.push_back(PathData('C',pt0, pt1+MWAWVec2f(dir[0],0), pt0-MWAWVec2f(0,dir[1])));
      pt1=MWAWVec2f(center[0],m_formBox[1][1]);
      res.push_back(PathData('C',pt1, pt0+MWAWVec2f(0,dir[1]), pt1+MWAWVec2f(dir[0],0)));
      pt0=MWAWVec2f(m_formBox[0][0],center[1]);
      res.push_back(PathData('C',pt0, pt1-MWAWVec2f(dir[0],0), pt0+MWAWVec2f(0,dir[1])));
      res.push_back(PathData('Z'));
    }
    else {
      MWAWVec2f pt0 = MWAWVec2f(m_formBox[0][0],0.5f*(m_formBox[0][1]+m_formBox[1][1]));
      MWAWVec2f pt1 = MWAWVec2f(m_formBox[1][0],pt0[1]);
      res.push_back(PathData('M',pt0));
      PathData data('A',pt1);
      data.m_r=0.5f*(m_formBox[1]-m_formBox[0]);
      data.m_largeAngle=true;
      res.push_back(data);
      data.m_x=pt0;
      res.push_back(data);
    }
    break;
  }
  case Arc:
  case Pie: {
    MWAWVec2f center=0.5f*(m_formBox[0]+m_formBox[1]);
    MWAWVec2f rad=0.5f*(m_formBox[1]-m_formBox[0]);
    float angl0=m_arcAngles[0];
    float angl1=m_arcAngles[1];
    if (rad[1]<0) {
      static bool first=true;
      if (first) {
        MWAW_DEBUG_MSG(("MWAWGraphicShape::getPath: oops radiusY for arc is negative, inverse it\n"));
        first=false;
      }
      rad[1]=-rad[1];
    }
    while (angl1<angl0)
      angl1+=360.f;
    while (angl1>angl0+360.f)
      angl1-=360.f;
    if (angl1-angl0>=180.f && angl1-angl0<=180.f)
      angl1+=0.01f;
    float angl=angl0*float(M_PI/180.);
    bool addCenter=m_type==Pie;
    if (addCenter)
      res.push_back(PathData('M', center));
    MWAWVec2f pt=center+MWAWVec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
    res.push_back(PathData(addCenter ? 'L' : 'M', pt));
    if (!forTransformation) {
      angl=angl1*float(M_PI/180.);
      pt=center+MWAWVec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
      PathData data('A',pt);
      data.m_largeAngle=(angl1-angl0>=180.f);
      data.m_r=rad;
      res.push_back(data);
    }
    else {
      int N=int(angl1-angl0)/90;
      float dAngle=float(angl1-angl0)/float(N+1);
      for (int i=0; i<=N; ++i) {
        float newAngl= i==N ? angl1 : angl0+float(i+1)*dAngle;
        newAngl*=float(M_PI/180.);
        MWAWVec2f newPt=center+MWAWVec2f(std::cos(angl1)*rad[0],-std::sin(angl1)*rad[1]);
        MWAWVec2f dir(-std::sin(angl)*rad[0],-std::cos(angl)*rad[1]);
        MWAWVec2f newDir(-std::sin(newAngl)*rad[0],-std::cos(newAngl)*rad[1]);
        float deltaDir=4/3.f*std::tan((newAngl-angl)/4);
        res.push_back(PathData('C',newPt,pt+deltaDir*dir,newPt-deltaDir*newDir));
        pt=newPt;
        angl=newAngl;
      }
      if (m_type==Pie) res.push_back(PathData('Z'));
    }
    break;
  }
  case Path:
    return m_path;
  case ShapeUnknown:
#if !defined(__clang__)
  default:
#endif
    MWAW_DEBUG_MSG(("MWAWGraphicShape::getPath: unexpected type\n"));
    break;
  }
  return res;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: