Package net.sf.latexdraw.glib.views.Java2D.impl

Source Code of net.sf.latexdraw.glib.views.Java2D.impl.LShapeView

package net.sf.latexdraw.glib.views.Java2D.impl;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

import net.sf.latexdraw.glib.models.GLibUtilities;
import net.sf.latexdraw.glib.models.ShapeFactory;
import net.sf.latexdraw.glib.models.interfaces.shape.ILine;
import net.sf.latexdraw.glib.models.interfaces.shape.IPoint;
import net.sf.latexdraw.glib.models.interfaces.shape.IShape;
import net.sf.latexdraw.glib.models.interfaces.shape.IShape.FillingStyle;
import net.sf.latexdraw.glib.models.interfaces.shape.IShape.LineStyle;
import net.sf.latexdraw.glib.views.AbstractView;
import net.sf.latexdraw.glib.views.Java2D.interfaces.IViewArrow;
import net.sf.latexdraw.glib.views.Java2D.interfaces.IViewShape;
import net.sf.latexdraw.util.LNumber;

import org.malai.picking.Picker;

/**
* Defines a view of the LShape model.<br>
* <br>
* This file is part of LaTeXDraw.<br>
* Copyright (c) 2005-2014 Arnaud BLOUIN<br>
* <br>
* LaTeXDraw is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later version.
* <br>
* LaTeXDraw is distributed without any warranty; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.<br>
* <br>
* 02/16/2008<br>
* @author Arnaud BLOUIN
* @since 3.0
*/
abstract class LShapeView<S extends IShape> extends AbstractView<S> implements IViewShape {
  /** The Java2D path used to draw the shape. */
  protected Path2D path;

  /** The view of the arrows of the shape. */
  protected List<IViewArrow> arrows;

  /**
   * The border of the shape. This attribute must be used to compute
   * 'intersects', 'contains' operations, etc. operations.
   */
  protected Rectangle2D border;



  /**
   * Creates a view of the given model and initialises the Java2D view.
   * @param model The shape model.
   * @throws IllegalArgumentException If <code>model</code> is null.
   */
  protected LShapeView(final S model) {
    super(model);

    path    = new Path2D.Double();
    border   = new Rectangle2D.Double();
    arrows  = new ArrayList<>();
  }



  protected static Shape getRotatedShape2D(final double angle, final Shape shape, final IPoint tlPoint, final IPoint brPoint) {
    final Shape sh;

    if(LNumber.equalsDouble(angle, 0.))
      sh = shape;
    else {
      final double cx      = (tlPoint.getX()+brPoint.getX())/2.;
      final double cy      = (tlPoint.getY()+brPoint.getY())/2.;
      final double c2x      = Math.cos(angle)*cx - Math.sin(angle)*cy;
      final double c2y      = Math.sin(angle)*cx + Math.cos(angle)*cy;
      final AffineTransform at = AffineTransform.getTranslateInstance(cx - c2x, cy - c2y);
      at.rotate(angle);
      sh              = at.createTransformedShape(shape);
    }

    return sh;
  }


  @Override
  public Shape getRotatedShape2D() {
    return getRotatedShape2D(shape.getRotationAngle(), path, shape.getTopLeftPoint(), shape.getBottomRightPoint());
  }



  @Override
  public Path2D getPath() {
    return path;
  }


  @Override
  public void updateBorder() {
    final Shape sh;

    if(LNumber.equalsDouble(shape.getRotationAngle(), 0.))
      sh = path;
    else
      sh = getRotatedShape2D();

    border.setFrame(getStroke().createStrokedShape(sh).getBounds2D());
  }



  /**
   * Updates the borders of the shape as inside borders.
   * @since 3.0
   */
  protected abstract void updateGeneralPathInside();



  /**
   * Updates the borders of the shape as middle borders.
   * @since 3.0
   */
  protected abstract void updateGeneralPathMiddle();



  /**
   * Updates the borders of the shape as outside borders.
   * @since 3.0
   */
  protected abstract void updateGeneralPathOutside();



  @Override
  public void updatePath() {
    if(shape.isBordersMovable())
      if(shape.isDbleBorderable() && shape.hasDbleBord())
        switch(shape.getBordersPosition()) {
          case INTO  : updateDblePathInside(); break;
          case MID  : updateDblePathMiddle(); break;
          case OUT  : updateDblePathOutside(); break;
        }
      else
        switch(shape.getBordersPosition()) {
          case INTO  : updateGeneralPathInside(); break;
          case MID  : updateGeneralPathMiddle(); break;
          case OUT  : updateGeneralPathOutside(); break;
        }
    else
      if(shape.isDbleBorderable() && shape.hasDbleBord())
        updateDblePathMiddle();
      else
        updateGeneralPathMiddle();

    updatePathArrows();
  }


  /**
   * Updates the path of the view of the arrows.
   */
  protected void updatePathArrows() {
    for(final IViewArrow arrView : arrows)
      arrView.updatePath();
  }



  /**
   * Updates the borders of the double boundary when position is outside.
   * @since 3.0
   */
  protected abstract void updateDblePathOutside();



  /**
   * Updates the borders of the double boundary when position is inside.
   * @since 3.0
   */
  protected abstract void updateDblePathInside();



  /**
   * Updates the borders of the double boundary when position is middle.
   * @since 3.0
   */
  protected abstract void updateDblePathMiddle();



  @Override
  public void update() {
    updatePath();
    updateBorder();
  }


  @Override
  public void paintFilling(final Graphics2D g) {
    final FillingStyle fStyle = shape.getFillingStyle();

    switch(fStyle) {
      case NONE:
        if(shape.hasShadow() && shape.shadowFillsShape()) {
          g.setColor(shape.getFillingCol());
          g.fill(path);
        }

        break;

      case PLAIN:
        g.setColor(shape.getFillingCol());
        if(shape.hasShadow()) {
          g.setStroke(new BasicStroke((float)getStrokeThickness()));
          g.draw(path);
        }
        g.fill(path);
        break;

      case GRAD:
        final GeneralPath p = new GeneralPath(path);//TODO checks if useful the create an other path
        final IPoint tl  = shape.getTopLeftPoint();
        final IPoint br  = shape.getBottomRightPoint();
        IPoint pt1      = ShapeFactory.createPoint((tl.getX()+br.getX())/2., tl.getY());
        IPoint pt2      = ShapeFactory.createPoint((tl.getX()+br.getX())/2., br.getY());
        double angle    = shape.getGradAngle()%(2*Math.PI);
        double gradMidPt = shape.getGradMidPt();

        p.setWindingRule(Path2D.WIND_NON_ZERO);

        if(angle<0.)
          angle = 2.*Math.PI + angle;

        if(angle>=Math.PI) {
          gradMidPt = 1. - gradMidPt;
                    angle -= Math.PI;
        }

                if (LNumber.equalsDouble(angle, 0.)) {
                    if (gradMidPt < 0.5)
                        pt1.setY(pt2.getY() - Point2D.distance(pt2.getX(), pt2.getY(), (tl.getX() + br.getX()) / 2., br.getY()));

                    pt2.setY(tl.getY() + (br.getY() - tl.getY()) * gradMidPt);
                } else {
                    if (LNumber.equalsDouble(angle % (Math.PI / 2.), 0.)) {
                        pt1 = ShapeFactory.createPoint(tl.getX(), (tl.getY() + br.getY()) / 2.);
                        pt2 = ShapeFactory.createPoint(br.getX(), (tl.getY() + br.getY()) / 2.);

                        if (gradMidPt < 0.5)
                            pt1.setX(pt2.getX() - Point2D.distance(pt2.getX(), pt2.getY(), br.getX(), (tl.getY() + br.getY()) / 2.));

                        pt2.setX(tl.getX() + (br.getX() - tl.getX()) * gradMidPt);
                    } else {
                        final IPoint cg = shape.getGravityCentre();
                        final ILine l2;
                        final ILine l;

                        pt1 = pt1.rotatePoint(cg, -angle);
                        pt2 = pt2.rotatePoint(cg, -angle);
                        l = ShapeFactory.createLine(pt1, pt2);

                        if (angle >= 0. && angle < Math.PI / 2.)
                            l2 = l.getPerpendicularLine(tl);
                        else
                            l2 = l.getPerpendicularLine(ShapeFactory.createPoint(tl.getX(), br.getY()));

                        pt1 = l.getIntersection(l2);
                        final double distance = Point2D.distance(cg.getX(), cg.getY(), pt1.getX(), pt1.getY());
                        l.setX1(pt1.getX());
                        l.setY1(pt1.getY());
                        final IPoint[] pts = l.findPoints(pt1, 2 * distance * gradMidPt);
                        pt2 = pts[0];

                        if (gradMidPt < 0.5)
                            pt1 = pt1.rotatePoint(shape.getGravityCentre(), Math.PI);
                    }
                }//if(angle!=0)

        g.setPaint(new GradientPaint(
              (float)pt1.getX(), (float)pt1.getY(), shape.getGradColStart(),
              (float)pt2.getX(), (float)pt2.getY(), shape.getGradColEnd(),true));
        g.fill(p);
        break;

      case CLINES_PLAIN:
      case HLINES_PLAIN:
      case VLINES_PLAIN:
      case CLINES:
      case VLINES:
      case HLINES:
        final Shape oldClip = g.getClip();
        final Rectangle2D bounds  = path.getBounds2D();
        g.setClip(path);

        if(shape.isFilled() || shape.hasShadow() && shape.shadowFillsShape()) {
          g.setColor(shape.getFillingCol());
          g.fill(bounds);
        }

        final Stroke oldStroke = g.getStroke();
        final double hAngle   = shape.getHatchingsAngle();

        if(fStyle==FillingStyle.VLINES || fStyle==FillingStyle.VLINES_PLAIN)
          paintHatchings2(g, hAngle, bounds);
        else
          if(fStyle==FillingStyle.HLINES || fStyle==FillingStyle.HLINES_PLAIN)
          paintHatchings2(g, hAngle>0?hAngle-Math.PI/2.:hAngle+Math.PI/2., bounds);
        else
          if(fStyle==FillingStyle.CLINES || fStyle==FillingStyle.CLINES_PLAIN) {
            paintHatchings2(g, hAngle, bounds);
            paintHatchings2(g, hAngle>0?hAngle-Math.PI/2.:hAngle+Math.PI/2., bounds);
          }

        g.setStroke(oldStroke);
        g.setClip(oldClip);
        break;
    }
  }



  @Override
  public boolean contains(final double x, final double y) {
    // We test the borders first to limit the computations.
    if(!border.contains(x, y))
      return false;

    final Shape sh = LNumber.equalsDouble(shape.getRotationAngle(), 0.) ? path : getRotatedShape2D();

    if(shape.isFilled() && sh.contains(x, y))
      return true;

    final BasicStroke bc = getStroke();

    // We test if the point is on the shape.
    return bc!=null && bc.createStrokedShape(sh).contains(x, y);
  }



  @Override
  public boolean contains(final IPoint pt) {
    return pt!=null && contains(pt.getX(), pt.getY());
  }



  @Override
  public boolean intersects(final Rectangle2D rec) {
    if(rec==null || LNumber.equalsDouble(shape.getRotationAngle(), 0.) && !rec.contains(border) && !border.contains(rec) && !rec.intersects(border))
      return false;

    final BasicStroke stroke = getStroke();
    final Shape sh        = LNumber.equalsDouble(shape.getRotationAngle(), 0.) ? path : getRotatedShape2D();

    if(stroke==null)
      return sh.intersects(rec) || sh.contains(rec);

    return shape.isFilled() && sh.contains(rec) || stroke.createStrokedShape(sh).intersects(rec);
  }



  @Override
  public void paint(final Graphics2D g, final Rectangle clip) {
    if(clip!=null && !clip.contains(border) && !clip.intersects(border)) return;

    // We begin the rotation, if needed.
    final IPoint vectorTrans = beginRotation(g);
    final boolean isShowPts  = shape.isShowPtsable() && shape.isShowPts();

    // Lines of the show points option must be drawn before all.
    if(isShowPts)
      paintShowPointsLines(g);

    if(shape.isShadowable())
      paintShadow(g);

    if(shape.isFillable())
      paintFilling(g);

    paintBorders(g);

    // Painting the arrows of the shape.
    paintArrows(g, false);

    // Dots of the show points option must be drawn after all.
    if(isShowPts)
      paintShowPointsDots(g);

    // We close the rotation.
    if(vectorTrans!=null)
      endRotation(g, vectorTrans);
  }


  /**
   * Paints the arrows of the shape.
   * @since 3.0
   */
  protected void paintArrows(final Graphics2D g, final boolean asShadow) {
    final Color colour = asShadow ? shape.getShadowCol() : shape.getFillingCol();

    for(final IViewArrow arrow : arrows)
      arrow.paint(g, colour, asShadow);
  }



  @Override
  public void paintBorders(final Graphics2D g) {
    if(shape.getLineStyle()==LineStyle.NONE)
      return;

    if(shape.hasDbleBord())
      paintBordersDouble(g);
    else
      paintBordersSimple(g);
  }



  /**
   * Draws the double borders of the shape.
   * @param g The graphics to print into.
   * @since 3.0
   */
  private void paintBordersDouble(final Graphics2D g) {
    g.setStroke(getStroke());
    g.setColor(shape.getLineColour());
    g.draw(path);
    g.setColor(shape.getDbleBordCol());
    g.setStroke(new BasicStroke((float) shape.getDbleBordSep()));
    g.draw(path);
  }



  /**
   * Draws the simple border (not with double borders).
   * @param g The graphics to print into.
   * @since 3.0
   */
  private void paintBordersSimple(final Graphics2D g) {
    g.setColor(shape.getLineColour());
    g.setStroke(getStroke());
    g.draw(path);
  }



  @Override
  public void paintShadow(final Graphics2D g) {
    if(shape.hasShadow()) {
      final double dx;
      final double dy;
      final IPoint gc     = shape.getGravityCentre();
      final IPoint shadowgc   = ShapeFactory.createPoint(gc.getX()+shape.getShadowSize(), gc.getY());

      shadowgc.setPoint(shadowgc.rotatePoint(gc, shape.getShadowAngle()));
      dx = shadowgc.getX() - gc.getX();
      dy = gc.getY() - shadowgc.getY();

      g.setStroke(new BasicStroke((float) getStrokeThickness(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
      g.translate(dx, dy);
      g.setColor(shape.getShadowCol());
      g.draw(path);

      if(shape.isFilled() || shape.shadowFillsShape())
        g.fill(path);

      // Painting the arrows of the shadow.
      paintArrows(g, true);

      g.translate(-dx, -dy);
    }
  }



  @Override
  public void paintShowPointsLines(final Graphics2D g) {
    // TODO Auto-generated method stub
  }



  @Override
  public void paintShowPointsDots(final Graphics2D g) {
    // TODO Auto-generated method stub
  }



  /**
   * Paints the hatchings.
   * @param g The graphics to paint.
   * @param angle The angle of the hatchings (in radian).
   * @param clip The clip box.
   */
  private void paintHatchings2(final Graphics2D g, final double angle, final Rectangle2D clip) {
    if(g==null || clip==null)
      return ;

    double angle2 = angle%(Math.PI*2.);
    final float halphPI = (float)(Math.PI/2.);

    if(angle2>0) {
      if((float)angle2>3f*halphPI)
                angle2 -= Math.PI * 2.;
      else
        if((float)angle2>halphPI)
                    angle2 -= Math.PI;
    }
    else
      if((float)angle2<-3f*halphPI)
                angle2 += Math.PI * 2.;
      else
        if((float)angle2<-halphPI)
                    angle2 += Math.PI;

    final Line2D.Double line  = new Line2D.Double();
    final double val      = shape.getHatchingsWidth()+shape.getHatchingsSep();
    final float fAngle    = (float)angle2;

    g.setStroke(new BasicStroke((float)shape.getHatchingsWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
    g.setPaint(shape.getHatchingsCol());

    if(fAngle==0f) {
      line.y1   = clip.getMinY();
      line.y2   = clip.getMaxY();
      final double maxX = clip.getMaxX();

      for(double x = clip.getMinX(); x<maxX; x+=val) {
        line.x1 = x;
        line.x2 = x;
        g.draw(line);
      }
    }
    else
      if(fAngle==halphPI || fAngle==-halphPI) {
        line.x1   = clip.getMinX();
        line.x2   = clip.getMaxX();
        final double maxY = clip.getMaxY();

        for(double y = clip.getMinY(); y<maxY; y+=val) {
          line.y1 = y;
          line.y2 = y;
          g.draw(line);
        }
      }
      else {
        final double incX = val/Math.cos(angle2);
        final double incY = val/Math.sin(angle2);
        final double maxX;

        if(fAngle>0f) {
          line.y1 = clip.getMinY();
          maxX   = clip.getMaxX() + (clip.getMaxY()-(clip.getMinY()<0?clip.getMinY():0)) * Math.tan(angle2);
        }
        else {
          line.y1 = clip.getMaxY();
          maxX   = clip.getMaxX() - clip.getMaxY() * Math.tan(angle2);
        }

        line.x1 = clip.getMinX();
        line.x2 = line.x1;
        line.y2 = line.y1;

        if((float)incX<=0f)
          return ;

        while(line.x2 < maxX) {
          line.x2 += incX;
          line.y1 += incY;
          g.draw(line);
        }
      }
  }



  @Override
  public BasicStroke getStroke() {
    final float strokeTh  = (float) getStrokeThickness();
    final BasicStroke stroke;

    switch(shape.getLineStyle()) {
      case SOLID:
        stroke = new BasicStroke(strokeTh, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        break;

      case DASHED:
        stroke = new BasicStroke(strokeTh, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1f,
             new float[] { (float)shape.getDashSepBlack(), (float)shape.getDashSepWhite() }, 0f);
        break;

      case DOTTED:
        final float thickness = (float)shape.getThickness();
        // The size of the dots of the stroke depends on the thickness and eventually on the double borders size.
        final float dot = (float)shape.getDotSep() +
                (shape.hasDbleBord() ? thickness*2f + (float)shape.getDbleBordSep() : thickness);

        stroke = new BasicStroke(strokeTh, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER, 1f, new float[] { 0f, dot }, 0f);
        break;

      case NONE:
      default:
        stroke = null;
        break;
    }

    return stroke;
  }



  /**
   * @return The thickness of the stroke; The difference with shape.getThickness is that
   * the double borders and their size are took in account.
   * @since 3.0
   */
  protected double getStrokeThickness() {
    final double thickness = shape.getThickness();
    return  shape.hasDbleBord() ? thickness*2. + shape.getDbleBordSep() : thickness;
  }



  /**
   * Begins the rotation by modifying the Graphics2D using the rotation angle.
   * @param g The graphics to modify to draw rotated views.
   * @return The transition vector used to place the rotated view to its original location.
   * The rotation was made as the origin point, so as translation is needed to
   * place the view at the centre of the view. This point will be used to end the rotation.
   * Returns null if g is null or if the rotation angle is equal to 0.
   * @since 3.0
   */
  protected IPoint beginRotation(final Graphics2D g) {
    final double rotationAngle = shape.getRotationAngle();
    IPoint p = null;

    if(!LNumber.equalsDouble(rotationAngle%(Math.PI*2.), 0.) && g!=null) {
      final IPoint tl = shape.getTopLeftPoint();//FIXME: should be border?
      final IPoint br = shape.getBottomRightPoint();
      final double cx = (tl.getX() + br.getX()) / 2.;
            final double cy = (tl.getY() + br.getY()) / 2.;
            final double c2x = Math.cos(rotationAngle) * cx - Math.sin(rotationAngle)* cy;
      final double c2y = Math.sin(rotationAngle) * cx + Math.cos(rotationAngle)* cy;
      final double c3x = Math.cos(-rotationAngle) * (cx - c2x)- Math.sin(-rotationAngle) * (cy - c2y);
      final double c3y = Math.sin(-rotationAngle) * (cx - c2x)+ Math.cos(-rotationAngle) * (cy - c2y);

      g.rotate(rotationAngle);
      g.translate(c3x, c3y);
      p = ShapeFactory.createPoint(c3x, c3y);
    }

    return p;
  }



  /**
   * Ends the rotation of the view by modifying the Graphics2D using the rotation angle
   * of the model of the view, and the given translation vector.
   * @param g The graphics to un-rotate.
   * @param translation This translation vector is given by function beginRotation. It is
   * used to translate the graphics at the initial position.
   * @since 3.0
   */
  protected void endRotation(final Graphics2D g, final IPoint translation) {
    if(GLibUtilities.isValidPoint(translation) && g!=null) {
      g.translate(-translation.getX(), -translation.getY());
      g.rotate(-shape.getRotationAngle());
    }
  }


  /**
   * Gives the top-left and the bottom-right points of the rotated rectangle.
   * @param tlx The top-left x-coordinate of the rectangle to rotate.
   * @param tly The top-left y-coordinate of the rectangle to rotate.
   * @param width The width of the rectangle to rotate.
   * @param height The height of the rectangle to rotate.
   * @param angle The rotation angle.
   * @param gravityCentre The gravity centre used for the rotation.
   * @param tl The resulting top-left point. Must not be null.
   * @param br The resulting bottom-right point. Must not be null.
   * @since 3.0
   */
  protected static void getRotatedRectangle(final double tlx, final double tly, final double width,
                       final double height, final double angle, final IPoint gravityCentre,
                       final IPoint tl, final IPoint br) {
    final IPoint[] pts = new IPoint[4];
    // Rotation of the four points of the rectangle.
    pts[0] = ShapeFactory.createPoint(tlx, tly).rotatePoint(gravityCentre, angle);
    pts[1] = ShapeFactory.createPoint(tlx+width, tly).rotatePoint(gravityCentre, angle);
    pts[2] = ShapeFactory.createPoint(tlx+width, tly+height).rotatePoint(gravityCentre, angle);
    pts[3] = ShapeFactory.createPoint(tlx, tly+height).rotatePoint(gravityCentre, angle);
    tl.setPoint(Double.MAX_VALUE, Double.MAX_VALUE);
    br.setPoint(Double.MIN_VALUE, Double.MIN_VALUE);

    // Defining the border of the rotated rectangle.
        for(final IPoint pt : pts) {
            if (pt.getX() < tl.getX())
                tl.setX(pt.getX());
            if (pt.getX() > br.getX())
                br.setX(pt.getX());
            if (pt.getY() < tl.getY())
                tl.setY(pt.getY());
            if (pt.getY() > br.getY())
                br.setY(pt.getY());
        }
  }



  @Override
  public Rectangle2D getBorder() {
    return border;
  }


  @Override
  public Picker getPicker() {
    return null;
  }


  @Override
  public void flush() {
    shape = null;
    path = null;
    border = null;

    if(arrows!=null) {
      arrows.clear();
      arrows = null;
    }
  }
}
TOP

Related Classes of net.sf.latexdraw.glib.views.Java2D.impl.LShapeView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.