blob: 134e02a36948faa0f02dbc980aa3a287be8758b3 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.uibuilder.graphics;
import com.android.annotations.NonNull;
import com.android.tools.idea.uibuilder.model.AndroidCoordinate;
import com.android.tools.idea.uibuilder.model.Coordinates;
import com.android.tools.idea.uibuilder.model.SwingCoordinate;
import com.android.tools.idea.uibuilder.surface.ScreenView;
import java.awt.*;
public class NlGraphics {
private final ScreenView myScreenView;
private final Graphics2D myGraphics;
public NlGraphics(@NonNull Graphics2D graphics, @NonNull ScreenView screenView) {
myGraphics = graphics;
myScreenView = screenView;
}
@NonNull
public Graphics2D getSwingGraphics() {
return myGraphics;
}
@NonNull
public ScreenView getScreenView() {
return myScreenView;
}
/**
* Current style being used for drawing.
*/
@NonNull
private NlDrawingStyle myStyle = NlDrawingStyle.INVALID;
/** Use the given style for subsequent drawing operations */
public void useStyle(@NonNull NlDrawingStyle style) {
myStyle = style;
}
public void fillRect(@AndroidCoordinate int x,
@AndroidCoordinate int y,
@AndroidCoordinate int width,
@AndroidCoordinate int height) {
Color fillColor = myStyle.getFillColor();
if (fillColor != null) {
useFill(myStyle, myGraphics);
x = Coordinates.getSwingX(myScreenView, x);
y = Coordinates.getSwingY(myScreenView, y);
width = Coordinates.getSwingDimension(myScreenView, width);
height = Coordinates.getSwingDimension(myScreenView, height);
fillRect(myStyle, myGraphics, x, y, width, height);
}
}
public void drawLine(@AndroidCoordinate int x1,
@AndroidCoordinate int y1,
@AndroidCoordinate int x2,
@AndroidCoordinate int y2) {
x1 = Coordinates.getSwingX(myScreenView, x1);
x2 = Coordinates.getSwingX(myScreenView, x2);
y1 = Coordinates.getSwingY(myScreenView, y1);
y2 = Coordinates.getSwingY(myScreenView, y2);
drawLine(myStyle, myGraphics, x1, y1, x2, y2);
}
public void drawRect(@AndroidCoordinate int x,
@AndroidCoordinate int y,
@AndroidCoordinate int width,
@AndroidCoordinate int height) {
x = Coordinates.getSwingX(myScreenView, x);
y = Coordinates.getSwingY(myScreenView, y);
width = Coordinates.getSwingDimension(myScreenView, width);
height = Coordinates.getSwingDimension(myScreenView, height);
drawRect(myStyle, myGraphics, x, y, width, height);
}
public void drawArrow(@AndroidCoordinate int x1,
@AndroidCoordinate int y1,
@AndroidCoordinate int x2,
@AndroidCoordinate int y2) {
x1 = Coordinates.getSwingX(myScreenView, x1);
x2 = Coordinates.getSwingX(myScreenView, x2);
y1 = Coordinates.getSwingY(myScreenView, y1);
y2 = Coordinates.getSwingY(myScreenView, y2);
drawArrow(myStyle, myGraphics, x1, y1, x2, y2);
}
public void drawCross(@AndroidCoordinate int radius) {
radius = Coordinates.getSwingDimension(myScreenView, radius);
drawCross(myStyle, myGraphics, radius);
}
// Swing coordinate system
public static void fillRect(@NonNull NlDrawingStyle style,
@NonNull Graphics gc,
@SwingCoordinate int x,
@SwingCoordinate int y,
@SwingCoordinate int width,
@SwingCoordinate int height) {
Color fillColor = style.getFillColor();
if (fillColor != null) {
useFill(style, gc);
gc.fillRect(x + 1, y + 1, width - 1, height - 1);
}
}
public static void drawFilledRect(@NonNull NlDrawingStyle style,
@NonNull Graphics gc,
@SwingCoordinate int x,
@SwingCoordinate int y,
@SwingCoordinate int width,
@SwingCoordinate int height) {
Color fillColor = style.getFillColor();
if (fillColor != null) {
useFill(style, gc);
gc.setColor(fillColor);
gc.fillRect(x + 1, y + 1, width - 2, height - 2);
}
useStroke(style, gc);
if (style.getStrokeColor() != null) {
gc.drawRect(x, y, width - 1, height - 1);
}
}
public static void drawStrokeFilledRect(@NonNull NlDrawingStyle style,
@NonNull Graphics gc,
@SwingCoordinate int x,
@SwingCoordinate int y,
@SwingCoordinate int width,
@SwingCoordinate int height) {
useStroke(style, gc);
if (style.getStrokeColor() != null) {
gc.fillRect(x, y, width, height);
}
}
public static void drawRect(@NonNull NlDrawingStyle style,
@NonNull Graphics gc,
@SwingCoordinate int x,
@SwingCoordinate int y,
@SwingCoordinate int width,
@SwingCoordinate int height) {
useStroke(style, gc);
gc.drawRect(x, y, width - 1, height - 1);
}
public static void drawLine(@NonNull NlDrawingStyle style,
@NonNull Graphics gc,
@SwingCoordinate int x1,
@SwingCoordinate int y1,
@SwingCoordinate int x2,
@SwingCoordinate int y2) {
useStroke(style, gc);
gc.drawLine(x1, y1, x2, y2);
}
private static final int MIN_LENGTH = 10;
private static final int ARROW_SIZE = 5;
public static void drawArrow(@NonNull NlDrawingStyle style,
@NonNull Graphics graphics,
@SwingCoordinate int x1,
@SwingCoordinate int y1,
@SwingCoordinate int x2,
@SwingCoordinate int y2) {
Color strokeColor = style.getStrokeColor();
if (strokeColor != graphics.getColor()) {
graphics.setColor(strokeColor);
}
if (graphics instanceof Graphics2D) {
Graphics2D gc2 = (Graphics2D)graphics;
Stroke stroke = style.getStroke();
if (gc2.getStroke() != stroke) {
gc2.setStroke(stroke);
}
}
int arrowWidth = ARROW_SIZE;
int arrowHeight = ARROW_SIZE;
// Make ARROW_SIZE adjustments to ensure that the arrow has enough width to be visible
if (x1 == x2 && Math.abs(y1 - y2) < MIN_LENGTH) {
int delta = (MIN_LENGTH - Math.abs(y1 - y2)) / 2;
if (y1 < y2) {
y1 -= delta;
y2 += delta;
} else {
y1 += delta;
y2-= delta;
}
} else if (y1 == y2 && Math.abs(x1 - x2) < MIN_LENGTH) {
int delta = (MIN_LENGTH - Math.abs(x1 - x2)) / 2;
if (x1 < x2) {
x1 -= delta;
x2 += delta;
} else {
x1 += delta;
x2-= delta;
}
}
graphics.drawLine(x1, y1, x2, y2);
// Arrowhead:
if (x1 == x2) {
// Vertical
if (y2 > y1) {
graphics.drawLine(x2 - arrowWidth, y2 - arrowHeight, x2, y2);
graphics.drawLine(x2 + arrowWidth, y2 - arrowHeight, x2, y2);
} else {
graphics.drawLine(x2 - arrowWidth, y2 + arrowHeight, x2, y2);
graphics.drawLine(x2 + arrowWidth, y2 + arrowHeight, x2, y2);
}
} else if (y1 == y2) {
// Horizontal
if (x2 > x1) {
graphics.drawLine(x2 - arrowHeight, y2 - arrowWidth, x2, y2);
graphics.drawLine(x2 - arrowHeight, y2 + arrowWidth, x2, y2);
} else {
graphics.drawLine(x2 + arrowHeight, y2 - arrowWidth, x2, y2);
graphics.drawLine(x2 + arrowHeight, y2 + arrowWidth, x2, y2);
}
} else {
// Compute angle:
int dy = y2 - y1;
int dx = x2 - x1;
double angle = Math.atan2(dy, dx);
double lineLength = Math.sqrt(dy * dy + dx * dx);
// Imagine a line of the same length as the arrow, but with angle 0.
// Its two arrow lines are at (-arrowWidth, -arrowHeight) relative
// to the endpoint (x1 + lineLength, y1) stretching up to (x2,y2).
// We compute the positions of (ax,ay) for the point above and
// below this line and paint the lines to it:
double ax = x1 + lineLength - arrowHeight;
double ay = y1 - arrowWidth;
int rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1);
int ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1);
graphics.drawLine(x2, y2, rx, ry);
ay = y1 + arrowWidth;
rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1);
ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1);
graphics.drawLine(x2, y2, rx, ry);
}
}
public static void drawCross(@NonNull NlDrawingStyle style, @NonNull Graphics g, @SwingCoordinate int radius) {
int size2 = (radius - 3) / 2;
Color fillColor = style.getFillColor();
if (fillColor != null) {
fillRect(style, g, 0, size2, radius, 3);
fillRect(style, g, size2, 0, 3, radius);
} else {
drawLine(style, g, 0, size2 + 1, radius, size2 + 1);
drawLine(style, g, size2 + 1, 0, size2 + 1, radius);
}
}
public static void useStroke(@NonNull NlDrawingStyle style, @NonNull Graphics gc) {
Color strokeColor = style.getStrokeColor();
if (strokeColor != gc.getColor()) {
gc.setColor(strokeColor);
}
if (gc instanceof Graphics2D) {
Graphics2D gc2 = (Graphics2D)gc;
Stroke stroke = style.getStroke();
if (gc2.getStroke() != stroke) {
gc2.setStroke(stroke);
}
}
}
public static void useFill(@NonNull NlDrawingStyle style, @NonNull Graphics gc) {
Color fillColor = style.getFillColor();
if (fillColor != null) {
if (fillColor != gc.getColor()) {
gc.setColor(fillColor);
}
}
}
}