/**
* ORIPA - Origami Pattern Editor
* Copyright (C) 2005-2009 Jun Mitani http://mitani.cs.tsukuba.ac.jp/
This program 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 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oripa.view.main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.vecmath.Vector2d;
import oripa.ORIPA;
import oripa.doc.Doc;
import oripa.fold.FoldedModelInfo;
import oripa.fold.OrigamiModel;
import oripa.mouse.MouseUtility;
import oripa.paint.EditMode;
import oripa.paint.core.LineSetting;
import oripa.paint.core.PaintConfig;
import oripa.paint.core.PaintContext;
import oripa.paint.creasepattern.CreasePattern;
import oripa.paint.util.ElementSelector;
import oripa.value.OriLine;
import oripa.viewsetting.ViewScreenUpdater;
import oripa.viewsetting.main.MainScreenSettingDB;
import oripa.viewsetting.main.ScreenUpdater;
public class PainterScreen extends JPanel
implements MouseListener, MouseMotionListener, MouseWheelListener,
ActionListener, ComponentListener, Observer{
private MainScreenSettingDB setting = MainScreenSettingDB.getInstance();
private ScreenUpdater screenUpdater = ScreenUpdater.getInstance();
private PaintContext mouseContext = PaintContext.getInstance();
private boolean bDrawFaceID = false;
private Image bufferImage;
private Graphics2D bufferg;
private Point2D preMousePoint; // Screen coordinates
private Point2D.Double currentMousePointLogic = new Point2D.Double(); // Logic coordinates
private double scale;
private double transX;
private double transY;
// Temporary information when editing
// Affine transformation information
private Dimension preSize;
private AffineTransform affineTransform = new AffineTransform();
private ArrayList<Vector2d> crossPoints = new ArrayList<>();
private JPopupMenu popup = new JPopupMenu();
private JMenuItem popupItem_DivideFace = new JMenuItem("Dividing face");
private JMenuItem popupItem_FlipFace = new JMenuItem("Flipping face");
public PainterScreen() {
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
addComponentListener(this);
screenUpdater.addObserver(this);
setting.addObserver(this);
scale = 1.5;
setBackground(Color.white);
popupItem_DivideFace.addActionListener(this);
popup.add(popupItem_DivideFace);
popupItem_FlipFace.addActionListener(this);
popup.add(popupItem_FlipFace);
preSize = getSize();
}
/**
* for verifying algorithm
* @param g2d
*/
// public void drawModel(Graphics2D g2d) {
//
// if (! Config.FOR_STUDY) {
// return;
// }
//
// Doc document = ORIPA.doc;
// OrigamiModel origamiModel = document.getOrigamiModel();
//
// List<OriFace> faces = origamiModel.getFaces();
// List<OriVertex> vertices = origamiModel.getVertices();
//
//
// if (bDrawFaceID) {
// g2d.setColor(Color.BLACK);
// for (OriFace face : faces) {
// g2d.drawString("" + face.tmpInt, (int) face.getCenter().x,
// (int) face.getCenter().y);
// }
// }
//
// g2d.setColor(new Color(255, 210, 220));
// for (OriFace face : faces) {
// if (face.tmpInt2 == 0) {
// g2d.setColor(Color.RED);
// g2d.fill(face.preOutline);
// } else {
// g2d.setColor(face.color);
// }
//
// if (face.hasProblem) {
// g2d.setColor(Color.RED);
// } else {
// if (face.faceFront) {
// g2d.setColor(new Color(255, 200, 200));
// } else {
// g2d.setColor(new Color(200, 200, 255));
// }
// }
//
//// g2d.fill(face.preOutline);
// }
//
// g2d.setColor(Color.BLACK);
//
//
// for (OriFace face : faces) {
// g2d.drawString("" + face.z_order, (int) face.getCenter().x,
// (int) face.getCenter().y);
// }
//
// g2d.setColor(Color.RED);
// for (OriVertex v : vertices) {
// if (v.hasProblem) {
// g2d.fill(new Rectangle2D.Double(v.p.x - 8.0 / scale,
// v.p.y - 8.0 / scale, 16.0 / scale, 16.0 / scale));
// }
// }
// }
// update actual AffineTransform
private void updateAffineTransform() {
affineTransform.setToIdentity();
affineTransform.translate(getWidth() * 0.5, getHeight() * 0.5);
affineTransform.scale(scale, scale);
affineTransform.translate(transX, transY);
}
public Image getCreasePatternImage(){
return bufferImage;
}
private void drawLines(Graphics2D g2d, Collection<OriLine> lines){
ElementSelector selector = new ElementSelector();
for (OriLine line : lines) {
if (line.typeVal == OriLine.TYPE_NONE &&!PaintConfig.dispAuxLines) {
continue;
}
if ((line.typeVal == OriLine.TYPE_RIDGE || line.typeVal == OriLine.TYPE_VALLEY)
&& !PaintConfig.dispMVLines) {
continue;
}
g2d.setColor(selector.selectColorByLineType(line.typeVal));
g2d.setStroke(selector.selectStroke(line.typeVal));
if(PaintConfig.mouseAction != null){
if(mouseContext.getLines().contains(line) == false){
g2d.draw(new Line2D.Double(line.p0.x, line.p0.y, line.p1.x, line.p1.y));
}
}
}
}
void drawVertexRectangles(Graphics2D g2d){
CreasePattern creasePattern = ORIPA.doc.getCreasePattern();
g2d.setColor(Color.BLACK);
final double vertexDrawSize = 2.0;
for (OriLine line : creasePattern) {
if (!PaintConfig.dispAuxLines && line.typeVal == OriLine.TYPE_NONE) {
continue;
}
if (!PaintConfig.dispMVLines && (line.typeVal == OriLine.TYPE_RIDGE
|| line.typeVal == OriLine.TYPE_VALLEY)) {
continue;
}
Vector2d v0 = line.p0;
Vector2d v1 = line.p1;
g2d.fill(new Rectangle2D.Double(v0.x - vertexDrawSize / scale,
v0.y - vertexDrawSize / scale, vertexDrawSize * 2 / scale,
vertexDrawSize * 2 / scale));
g2d.fill(new Rectangle2D.Double(v1.x - vertexDrawSize / scale,
v1.y - vertexDrawSize / scale, vertexDrawSize * 2 / scale,
vertexDrawSize * 2 / scale));
}
}
// Scaling relative to the center of the screen
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufferImage == null) {
bufferImage = createImage(getWidth(), getHeight());
bufferg = (Graphics2D) bufferImage.getGraphics();
updateAffineTransform();
preSize = getSize();
}
// initialize the AffineTransform of bufferg
bufferg.setTransform(new AffineTransform());
// Clears the image buffer
bufferg.setColor(Color.WHITE);
bufferg.fillRect(0, 0, getWidth(), getHeight());
// set the AffineTransform of buffer
bufferg.setTransform(affineTransform);
Graphics2D g2d = bufferg;
Doc document = ORIPA.doc;
CreasePattern creasePattern = document.getCreasePattern();
OrigamiModel origamiModel = document.getOrigamiModel();
FoldedModelInfo foldedModelInfo = document.getFoldedModelInfo();
// boolean hasModel = origamiModel.hasModel();
// if (hasModel) {
// drawModel(g2d);
// }
if (setting.isGridVisible()) {
drawGridLine(g2d);
}
//g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(LineSetting.STROKE_VALLEY);
g2d.setColor(Color.black);
drawLines(g2d, creasePattern);
// Drawing of the vertices
if (PaintConfig.getMouseAction().getEditMode() == EditMode.VERTEX
|| PaintConfig.dispVertex) {
drawVertexRectangles(g2d);
}
for (Vector2d v : crossPoints) {
g2d.setColor(Color.RED);
g2d.fill(new Rectangle2D.Double(v.x - 5.0 / scale, v.y - 5.0 / scale,
10.0 / scale, 10.0 / scale));
}
if (PaintConfig.bDispCrossLine) {
List<OriLine> crossLines = document.getCrossLines();
if (!crossLines.isEmpty()) {
g2d.setStroke(LineSetting.STROKE_TMP_OUTLINE);
g2d.setColor(Color.MAGENTA);
for (OriLine line : crossLines) {
Vector2d v0 = line.p0;
Vector2d v1 = line.p1;
g2d.draw(new Line2D.Double(v0.x, v0.y, v1.x, v1.y));
}
}
}
// Line that links the pair of unsetled faces
// if (Config.FOR_STUDY) {
// List<OriFace> faces = origamiModel.getFaces();
//
// int[][] overlapRelation = foldedModelInfo.getOverlapRelation();
//
// if (overlapRelation != null) {
// g2d.setStroke(LineSetting.STROKE_RIDGE);
// g2d.setColor(Color.MAGENTA);
// int size = faces.size();
// for (int i = 0; i < size; i++) {
// for (int j = i + 1; j < size; j++) {
// if (overlapRelation[i][j] == Doc.UNDEFINED) {
// Vector2d v0 = faces.get(i).getCenter();
// Vector2d v1 = faces.get(j).getCenter();
// g2d.draw(new Line2D.Double(v0.x, v0.y, v1.x, v1.y));
//
// }
// }
// }
// }
// }
if (PaintConfig.mouseAction != null) {
PaintConfig.mouseAction.onDraw(g2d, mouseContext);
g.drawImage(bufferImage, 0, 0, this);
drawCandidatePosition(g);
}
else {
g.drawImage(bufferImage, 0, 0, this);
}
}
private void drawCandidatePosition(Graphics g){
Vector2d candidate = mouseContext.pickCandidateV;
if(candidate != null){
g.setColor(Color.BLACK);
g.drawString("(" + candidate.x +
"," + candidate.y + ")", 0, 10);
}
}
private void drawGridLine(Graphics2D g2d) {
g2d.setColor(Color.LIGHT_GRAY);
g2d.setStroke(LineSetting.STROKE_GRID);
double paperSize = ORIPA.doc.getPaperSize();
int lineNum = PaintConfig.gridDivNum;
double step = paperSize / lineNum;
for (int i = 1; i < lineNum; i++) {
g2d.draw(new Line2D.Double(
step * i - paperSize / 2.0, -paperSize / 2.0,
step * i - paperSize / 2.0, paperSize / 2.0));
g2d.draw(new Line2D.Double(
-paperSize / 2.0, step * i - paperSize / 2.0,
paperSize / 2.0, step * i - paperSize / 2.0));
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (PaintConfig.mouseAction == null) {
return;
}
if(javax.swing.SwingUtilities.isRightMouseButton(e)){
PaintConfig.mouseAction.onRightClick(
mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
}
else {
PaintConfig.mouseAction = PaintConfig.mouseAction.onLeftClick(
mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
}
}
@Override
public void mousePressed(MouseEvent e) {
if(PaintConfig.mouseAction == null){
return;
}
PaintConfig.mouseAction.onPress(mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
preMousePoint = e.getPoint();
}
@Override
public void mouseReleased(MouseEvent e) {
// Rectangular Selection
if(PaintConfig.mouseAction != null){
PaintConfig.mouseAction.onRelease(mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
}
repaint();
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mouseDragged(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0 && // zoom
(e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == MouseEvent.CTRL_DOWN_MASK) {
double moved = e.getX() - preMousePoint.getX() + e.getY() - preMousePoint.getY();
scale += moved / 150.0;
if (scale < 0.01) {
scale = 0.01;
}
preMousePoint = e.getPoint();
updateAffineTransform();
repaint();
} else if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
transX += (double) (e.getX() - preMousePoint.getX()) / scale;
transY += (double) (e.getY() - preMousePoint.getY()) / scale;
preMousePoint = e.getPoint();
updateAffineTransform();
repaint();
} else {
mouseContext.setLogicalMousePoint( MouseUtility.getLogicalPoint(affineTransform, e.getPoint()) );
PaintConfig.getMouseAction().onDrag(mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
repaint();
}
}
@Override
public void mouseMoved(MouseEvent e) {
// Gets the value of the current logical coordinates of the mouse
try {
affineTransform.inverseTransform(e.getPoint(), currentMousePointLogic);
} catch (Exception ex) {
return;
}
mouseContext.scale = scale;
mouseContext.dispGrid = setting.isGridVisible();
mouseContext.setLogicalMousePoint( MouseUtility.getLogicalPoint(affineTransform, e.getPoint()) );
if (PaintConfig.mouseAction == null) {
return;
}
PaintConfig.mouseAction.onMove(mouseContext, affineTransform, MouseUtility.isControlKeyPressed(e));
//this.mouseContext.pickCandidateV = Globals.mouseAction.onMove(mouseContext, affineTransform, e);
repaint();
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
double scale_ = (100.0 - e.getWheelRotation() * 5) / 100.0;
scale *= scale_;
updateAffineTransform();
repaint();
}
@Override
public void actionPerformed(ActionEvent ae) {
}
@Override
public void componentResized(ComponentEvent arg0) {
if (getWidth() <= 0 || getHeight() <= 0) {
return;
}
preSize = getSize();
// Update of the logical coordinates of the center of the screen
transX = transX - preSize.width * 0.5 + getWidth() * 0.5;
transY = transY - preSize.height * 0.5 + getHeight() * 0.5;
// Updating the image buffer
bufferImage = createImage(getWidth(), getHeight());
bufferg = (Graphics2D) bufferImage.getGraphics();
updateAffineTransform();
repaint();
}
@Override
public void componentMoved(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentShown(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentHidden(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void update(Observable o, Object arg) {
String name = o.toString();
if(name.equals(screenUpdater.getName())){
if(arg != null){
if(arg.equals(ViewScreenUpdater.REDRAW_REQUESTED)){
repaint();
}
}
}
}
}