Highlight connected areas through copper traces, leads, jumpers, etc...

This commit is contained in:
Branislav Stojkovic
2018-12-16 00:52:15 +01:00
parent d506feac27
commit 58a4ff987f
25 changed files with 1685 additions and 1364 deletions
@@ -1,148 +1,150 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC 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.
*
* DIYLC 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 DIYLC. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.diylc.common;
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
import javax.swing.Icon;
This file is part of DIYLC.
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.KeywordPolicy;
DIYLC 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.
/**
* Entity class used to describe a component type.
*
* @author Branislav Stojkovic
*
* @see IDIYComponent
*/
public class ComponentType {
DIYLC 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.
private String name;
private String description;
private CreationMethod creationMethod;
private String category;
private String namePrefix;
private String author;
private Icon icon;
private Class<? extends IDIYComponent<?>> instanceClass;
private double zOrder;
private boolean flexibleZOrder;
private boolean stretchable;
private BomPolicy bomPolicy;
private boolean autoEdit;
private IComponentTransformer transformer;
private KeywordPolicy keywordPolicy;
private String keywordTag;
private boolean continuity;
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
public ComponentType(String name, String description, CreationMethod creationMethod, String category,
String namePrefix, String author, Icon icon, Class<? extends IDIYComponent<?>> instanceClass, double zOrder,
boolean flexibleZOrder, boolean stretchable, BomPolicy bomPolicy, boolean autoEdit,
IComponentTransformer transformer, KeywordPolicy keywordPolicy, String keywordTag, boolean continuity) {
super();
this.name = name;
this.description = description;
this.creationMethod = creationMethod;
this.category = category;
this.namePrefix = namePrefix;
this.author = author;
this.icon = icon;
this.instanceClass = instanceClass;
this.zOrder = zOrder;
this.flexibleZOrder = flexibleZOrder;
this.stretchable = stretchable;
this.bomPolicy = bomPolicy;
this.autoEdit = autoEdit;
this.transformer = transformer;
this.keywordPolicy = keywordPolicy;
this.keywordTag = keywordTag;
this.continuity = continuity;
}
*/
package org.diylc.common;
import javax.swing.Icon;
import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent;
import org.diylc.core.annotations.BomPolicy;
import org.diylc.core.annotations.KeywordPolicy;
/**
* Entity class used to describe a component type.
*
* @author Branislav Stojkovic
*
* @see IDIYComponent
*/
public class ComponentType {
private String name;
private String description;
private CreationMethod creationMethod;
private String category;
private String namePrefix;
private String author;
private Icon icon;
private Class<? extends IDIYComponent<?>> instanceClass;
private double zOrder;
private boolean flexibleZOrder;
private boolean stretchable;
private BomPolicy bomPolicy;
private boolean autoEdit;
private IComponentTransformer transformer;
private KeywordPolicy keywordPolicy;
private String keywordTag;
public ComponentType(String name, String description, CreationMethod creationMethod, String category,
String namePrefix, String author, Icon icon, Class<? extends IDIYComponent<?>> instanceClass, double zOrder,
boolean flexibleZOrder, boolean stretchable, BomPolicy bomPolicy, boolean autoEdit, IComponentTransformer transformer,
KeywordPolicy keywordPolicy, String keywordTag) {
super();
this.name = name;
this.description = description;
this.creationMethod = creationMethod;
this.category = category;
this.namePrefix = namePrefix;
this.author = author;
this.icon = icon;
this.instanceClass = instanceClass;
this.zOrder = zOrder;
this.flexibleZOrder = flexibleZOrder;
this.stretchable = stretchable;
this.bomPolicy = bomPolicy;
this.autoEdit = autoEdit;
this.transformer = transformer;
this.keywordPolicy = keywordPolicy;
this.keywordTag = keywordTag;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public CreationMethod getCreationMethod() {
return creationMethod;
}
public String getCategory() {
return category;
}
public String getNamePrefix() {
return namePrefix;
}
public String getAuthor() {
return author;
}
public Icon getIcon() {
return icon;
}
public Class<? extends IDIYComponent<?>> getInstanceClass() {
return instanceClass;
}
public double getZOrder() {
return zOrder;
}
public boolean isFlexibleZOrder() {
return flexibleZOrder;
}
public boolean isStretchable() {
return stretchable;
}
public BomPolicy getBomPolicy() {
return bomPolicy;
}
public boolean isAutoEdit() {
return autoEdit;
}
public IComponentTransformer getTransformer() {
return transformer;
}
public KeywordPolicy getKeywordPolicy() {
return keywordPolicy;
}
public String getKeywordTag() {
return keywordTag;
}
@Override
public String toString() {
return name;
}
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public CreationMethod getCreationMethod() {
return creationMethod;
}
public String getCategory() {
return category;
}
public String getNamePrefix() {
return namePrefix;
}
public String getAuthor() {
return author;
}
public Icon getIcon() {
return icon;
}
public Class<? extends IDIYComponent<?>> getInstanceClass() {
return instanceClass;
}
public double getZOrder() {
return zOrder;
}
public boolean isFlexibleZOrder() {
return flexibleZOrder;
}
public boolean isStretchable() {
return stretchable;
}
public BomPolicy getBomPolicy() {
return bomPolicy;
}
public boolean isAutoEdit() {
return autoEdit;
}
public IComponentTransformer getTransformer() {
return transformer;
}
public KeywordPolicy getKeywordPolicy() {
return keywordPolicy;
}
public String getKeywordTag() {
return keywordTag;
}
public boolean isContinuity() {
return continuity;
}
@Override
public String toString() {
return name;
}
}
@@ -70,7 +70,8 @@ public interface IPlugInPort extends ISelectionProcessor, IMouseProcessor, IKeyP
public static final String RECENT_COMPONENTS_KEY = "recentComponents";
public static final String RECENT_FILES_KEY = "recentFiles";
public static final String SHOW_RULERS_KEY = "showRulers";
public static final String SHOW_GRID_KEY = "showGrid";
public static final String SHOW_GRID_KEY = "showGrid";
public static final String HIGHLIGHT_CONTINUITY_AREA = "highlightContinuityArea";
public static final int DND_TOGGLE_STICKY = 0x1;
public static final int DND_TOGGLE_SNAP = 0x40000000;
@@ -61,7 +61,7 @@ public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComp
* @param componentState
* @param theme
*/
protected abstract void drawCurve(CubicCurve2D curve, Graphics2D g2d, ComponentState componentState);
protected abstract void drawCurve(CubicCurve2D curve, Graphics2D g2d, ComponentState componentState, IDrawingObserver drawingObserver);
/**
* @return default color.
@@ -110,7 +110,7 @@ public abstract class AbstractCurvedComponent<T> extends AbstractTransparentComp
// g2d.setColor(color);
// g2d.setStroke(new BasicStroke(thickness - 2));
// g2d.draw(path);
drawCurve(curve, g2d, componentState);
drawCurve(curve, g2d, componentState, drawingObserver);
g2d.setComposite(oldComposite);
}
@@ -32,7 +32,8 @@ import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.Observable;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.Display;
import org.diylc.common.IPlugInPort;
@@ -84,6 +85,10 @@ public abstract class AbstractLeadedComponent<T> extends AbstractTransparentComp
} catch (NullPointerException e) {
// This will happen if components do not have any shape.
}
}
protected boolean IsCopperArea() {
return false;
}
@Override
@@ -93,7 +98,7 @@ public abstract class AbstractLeadedComponent<T> extends AbstractTransparentComp
Shape shape = getBodyShape();
// If there's no body, just draw the line connecting the ending points.
if (shape == null) {
drawLead(g2d, componentState);
drawLead(g2d, componentState, drawingObserver, IsCopperArea());
} else if (supportsStandingMode() && length.convertToPixels() > points[0].distance(points[1])) {
// When ending points are too close draw the component in standing
// mode.
@@ -124,7 +129,7 @@ public abstract class AbstractLeadedComponent<T> extends AbstractTransparentComp
g2d.setColor(finalBorderColor);
g2d.draw(body);
if (!outlineMode) {
drawLead(g2d, componentState);
drawLead(g2d, componentState, drawingObserver, false);
}
} else {
// Normal mode with component body in the center and two lead parts.
@@ -293,12 +298,17 @@ public abstract class AbstractLeadedComponent<T> extends AbstractTransparentComp
g2d.drawLine(points[1].x, points[1].y, endX, endY);
}
private void drawLead(Graphics2D g2d, ComponentState componentState) {
private void drawLead(Graphics2D g2d, ComponentState componentState, IDrawingObserver observer, boolean isCopperArea) {
if (isCopperArea)
observer.startTrackingContinuityArea(true);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(getLeadThickness()));
Color leadColor =
shouldShadeLeads() ? getLeadColorForPainting(componentState).darker() : getLeadColorForPainting(componentState);
g2d.setColor(leadColor);
g2d.drawLine(points[0].x, points[0].y, points[1].x, points[1].y);
g2d.drawLine(points[0].x, points[0].y, points[1].x, points[1].y);
if (isCopperArea)
observer.stopTrackingContinuityArea();
if (shouldShadeLeads()) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(getLeadThickness() - 2));
@@ -31,5 +31,9 @@ public interface IDrawingObserver {
void stopTracking();
void startTracking();
void startTracking();
void stopTrackingContinuityArea();
void startTrackingContinuityArea(boolean positive);
}
@@ -116,6 +116,13 @@ public @interface ComponentDescriptor {
*
* @return
*/
String keywordTag() default "";
String keywordTag() default "";
/**
* For components that show continuity between the two ending points, e.d. wires, jumpers, etc.
*
* @return
*/
boolean continuity() default false;
}
@@ -0,0 +1,51 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC 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.
DIYLC 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 DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.presenter;
import java.awt.geom.Area;
import java.util.List;
public class ComponentArea {
private Area outlineArea;
private List<Area> continuityPositiveAreas;
private List<Area> continuityNegativeAreas;
public ComponentArea(Area outlineArea, List<Area> continuityPositiveAreas, List<Area> continuityNegativeAreas) {
super();
this.outlineArea = outlineArea;
this.continuityPositiveAreas = continuityPositiveAreas;
this.continuityNegativeAreas = continuityNegativeAreas;
}
public Area getOutlineArea() {
return outlineArea;
}
public List<Area> getContinuityPositiveAreas() {
return continuityPositiveAreas;
}
public List<Area> getContinuityNegativeAreas() {
return continuityNegativeAreas;
}
}
@@ -97,7 +97,8 @@ public class ComponentProcessor {
boolean autoEdit;
IComponentTransformer transformer;
KeywordPolicy keywordPolicy;
String keywordTag;
String keywordTag;
boolean continuity;
if (clazz.isAnnotationPresent(ComponentDescriptor.class)) {
ComponentDescriptor annotation = clazz.getAnnotation(ComponentDescriptor.class);
name = annotation.name();
@@ -113,7 +114,8 @@ public class ComponentProcessor {
autoEdit = annotation.autoEdit();
transformer = getComponentTransformer(annotation.transformer());
keywordPolicy = annotation.keywordPolicy();
keywordTag = annotation.keywordTag();
keywordTag = annotation.keywordTag();
continuity = annotation.continuity();
} else { // default
name = clazz.getSimpleName();
description = "";
@@ -128,7 +130,8 @@ public class ComponentProcessor {
autoEdit = true;
transformer = null;
keywordPolicy = KeywordPolicy.NEVER_SHOW;
keywordTag = null;
keywordTag = null;
continuity = false;
}
icon = null;
// Draw component icon.
@@ -148,7 +151,7 @@ public class ComponentProcessor {
}
ComponentType componentType =
new ComponentType(name, description, creationMethod, category, namePrefix, author, icon, clazz, zOrder,
flexibleZOrder, stretchable, bomPolicy, autoEdit, transformer, keywordPolicy, keywordTag);
flexibleZOrder, stretchable, bomPolicy, autoEdit, transformer, keywordPolicy, keywordTag, continuity);
componentTypeMap.put(clazz.getName(), componentType);
return componentType;
}
@@ -1,403 +1,591 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC 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.
*
* DIYLC 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 DIYLC. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.diylc.presenter;
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
This file is part of DIYLC.
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.common.ComponentType;
import org.diylc.common.DrawOption;
import org.diylc.common.EventType;
import org.diylc.common.GridType;
import org.diylc.common.IComponentFiler;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
import org.diylc.utils.Constants;
DIYLC 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.
/**
* Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken
* by each drawn component.
*
* @author Branislav Stojkovic
*/
public class DrawingManager {
DIYLC 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.
private static final Logger LOG = Logger.getLogger(DrawingManager.class);
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
public static int CONTROL_POINT_SIZE = 7;
*/
package org.diylc.presenter;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.common.DrawOption;
import org.diylc.common.EventType;
import org.diylc.common.GridType;
import org.diylc.common.IComponentFiler;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.Theme;
import org.diylc.core.VisibilityPolicy;
import org.diylc.utils.Constants;
/**
* Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken
* by each drawn component.
*
* @author Branislav Stojkovic
*/
public class DrawingManager {
private static final Logger LOG = Logger.getLogger(DrawingManager.class);
public static int CONTROL_POINT_SIZE = 7;
public static final String ZOOM_KEY = "zoom";
public static String DEBUG_COMPONENT_AREAS = "org.diylc.debugComponentAreas";
public static Color CONTROL_POINT_COLOR = Color.blue;
public static Color SELECTED_CONTROL_POINT_COLOR = Color.green;
private Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
Constants.DEFAULT_THEME);
// Keeps Area object of each drawn component.
private Map<IDIYComponent<?>, Area> componentAreaMap;
// Maps components to the last state they are drawn in. Also, used to
// determine which components are invalidated when they are not in the map.
private Map<IDIYComponent<?>, ComponentState> lastDrawnStateMap;
private Composite slotComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
private Composite lockedComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
private List<IDIYComponent<?>> failedComponents = new ArrayList<IDIYComponent<?>>();
private double zoomLevel = 1d;// ConfigurationManager.getInstance().readDouble(ZOOM_KEY,
// 1d);
private MessageDispatcher<EventType> messageDispatcher;
private boolean debugComponentAreas;
public DrawingManager(MessageDispatcher<EventType> messageDispatcher) {
super();
this.messageDispatcher = messageDispatcher;
componentAreaMap = new HashMap<IDIYComponent<?>, Area>();
lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS);
debugComponentAreas = debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true");
}
/**
* Paints the project onto the canvas and returns the list of components that failed to draw.
*
* @param g2d
* @param project
* @param drawOptions
* @param filter
* @param selectionRect
* @param selectedComponents
* @param lockedComponents
* @param groupedComponents
* @param controlPointSlot
* @param componentSlot
* @param dragInProgress
* @return
*/
public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project, Set<DrawOption> drawOptions,
IComponentFiler filter, Rectangle selectionRect, Collection<IDIYComponent<?>> selectedComponents,
Set<IDIYComponent<?>> lockedComponents, Set<IDIYComponent<?>> groupedComponents, List<Point> controlPointSlot,
List<IDIYComponent<?>> componentSlot, boolean dragInProgress) {
failedComponents.clear();
if (project == null) {
return failedComponents;
}
double zoom = 1d;
if (drawOptions.contains(DrawOption.ZOOM)) {
zoom = zoomLevel;
} else {
zoom = 1 / Constants.PIXEL_SIZE;
}
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
if (drawOptions.contains(DrawOption.ANTIALIASING)) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
// RenderingHints.VALUE_STROKE_PURE);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
}
// AffineTransform initialTx = g2d.getTransform();
Dimension d = getCanvasDimensions(project, zoom, true);
g2dWrapper.setColor(theme.getBgColor());
g2dWrapper.fillRect(0, 0, d.width, d.height);
g2d.clip(new Rectangle(new Point(0, 0), d));
GridType gridType = GridType.LINES;
if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) {
double zoomStep = project.getGridSpacing().convertToPixels() * zoom;
if (gridType == GridType.CROSSHAIR) {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {
(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4));
} else if (gridType == GridType.DOT) {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {1f,
(float) zoomStep - 1}, 0f));
}
g2dWrapper.setColor(theme.getGridColor());
for (double i = zoomStep; i < d.width; i += zoomStep) {
g2dWrapper.drawLine((int) i, 0, (int) i, d.height - 1);
}
for (double j = zoomStep; j < d.height; j += zoomStep) {
g2dWrapper.drawLine(0, (int) j, d.width - 1, (int) j);
}
}
if (Math.abs(1.0 - zoom) > 1e-4) {
g2dWrapper.scale(zoom, zoom);
}
// Composite mainComposite = g2d.getComposite();
// Composite alphaComposite =
// AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
// g2dWrapper.resetTx();
// componentAreaMap.clear();
for (IDIYComponent<?> component : project.getComponents()) {
// Do not draw the component if it's filtered out.
if (filter != null && !filter.testComponent(component)) {
continue;
}
ComponentState state = ComponentState.NORMAL;
if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) {
if (dragInProgress) {
state = ComponentState.DRAGGING;
} else {
state = ComponentState.SELECTED;
}
}
// Do not track the area if component is not invalidated and was
// drawn in the same state.
boolean trackArea = lastDrawnStateMap.get(component) != state;
synchronized (g2d) {
g2dWrapper.startedDrawingComponent();
if (!trackArea) {
g2dWrapper.stopTracking();
}
// Draw locked components in a new composite.
if (lockedComponents.contains(component)) {
g2d.setComposite(lockedComposite);
}
// Draw the component through the g2dWrapper.
try {
component.draw(g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
} catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e);
failedComponents.add(component);
}
Area area = g2dWrapper.finishedDrawingComponent();
if (trackArea && area != null && !area.isEmpty()) {
componentAreaMap.put(component, area);
lastDrawnStateMap.put(component, state);
}
}
}
// Draw control points.
if (drawOptions.contains(DrawOption.CONTROL_POINTS)) {
// Draw unselected points first to make sure they are below.
if (dragInProgress || drawOptions.contains(DrawOption.OUTLINE_MODE)) {
for (IDIYComponent<?> component : project.getComponents()) {
for (int i = 0; i < component.getControlPointCount(); i++) {
VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i);
if ((groupedComponents.contains(component)
&& (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) || (!groupedComponents
.contains(component) && !selectedComponents.contains(component) && component
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) {
g2dWrapper.setColor(CONTROL_POINT_COLOR);
Point controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE - 2;
g2dWrapper.fillOval(controlPoint.x - pointSize / 2, controlPoint.y - pointSize / 2, pointSize, pointSize);
}
}
}
}
// Then draw the selected ones.
for (IDIYComponent<?> component : selectedComponents) {
for (int i = 0; i < component.getControlPointCount(); i++) {
if (!groupedComponents.contains(component)
&& (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) {
Point controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE;
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval(controlPoint.x - pointSize / 2, controlPoint.y - pointSize / 2, pointSize, pointSize);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval(controlPoint.x - CONTROL_POINT_SIZE / 2 + 1, controlPoint.y - CONTROL_POINT_SIZE / 2
+ 1, CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2);
}
}
}
}
// Draw component slot in a separate composite.
if (componentSlot != null) {
g2dWrapper.startedDrawingComponent();
g2dWrapper.setComposite(slotComposite);
for (IDIYComponent<?> component : componentSlot) {
try {
component.draw(g2dWrapper, ComponentState.NORMAL, drawOptions.contains(DrawOption.OUTLINE_MODE), project,
g2dWrapper);
} catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e);
failedComponents.add(component);
}
}
g2dWrapper.finishedDrawingComponent();
}
// Draw control points of the component in the slot.
if (controlPointSlot != null) {
for (Point point : controlPointSlot) {
if (point != null) {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval(point.x - CONTROL_POINT_SIZE / 2, point.y - CONTROL_POINT_SIZE / 2, CONTROL_POINT_SIZE,
CONTROL_POINT_SIZE);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval(point.x - CONTROL_POINT_SIZE / 2 + 1, point.y - CONTROL_POINT_SIZE / 2 + 1,
CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2);
}
}
}
// Go back to the original transformation and zoom in to draw the
// selection rectangle and other similar elements.
// g2d.setTransform(initialTx);
// if ((drawOptions.contains(DrawOption.ZOOM)) && (Math.abs(1.0 -
// zoomLevel) > 1e-4)) {
// g2d.scale(zoomLevel, zoomLevel);
// }
// At the end draw selection rectangle if needed.
if (drawOptions.contains(DrawOption.SELECTION) && (selectionRect != null)) {
g2d.setColor(Color.white);
g2d.draw(selectionRect);
g2d.setColor(Color.black);
g2d.setStroke(Constants.DASHED_STROKE);
g2d.draw(selectionRect);
}
// Draw component area for test
if (debugComponentAreas) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.setColor(Color.red);
for (Area area : componentAreaMap.values()) {
g2d.draw(area);
}
}
return failedComponents;
}
public double getZoomLevel() {
return zoomLevel;
}
public void setZoomLevel(double zoomLevel) {
this.zoomLevel = zoomLevel;
fireZoomChanged();
// ConfigurationManager.getInstance().writeValue(ZOOM_KEY, zoomLevel);
}
public void invalidateComponent(IDIYComponent<?> component) {
componentAreaMap.remove(component);
lastDrawnStateMap.remove(component);
}
public Area getComponentArea(IDIYComponent<?> component) {
return componentAreaMap.get(component);
}
public void clearComponentAreaMap() {
componentAreaMap.clear();
lastDrawnStateMap.clear();
}
public List<IDIYComponent<?>> findComponentsAt(Point point, Project project) {
List<IDIYComponent<?>> components = new ArrayList<IDIYComponent<?>>();
for (int i = 0; i < project.getComponents().size(); i++) {
Area area = componentAreaMap.get(project.getComponents().get(i));
if (area != null && area.contains(point)) {
components.add(0, project.getComponents().get(i));
}
}
return components;
}
public Dimension getCanvasDimensions(Project project, Double zoomLevel, boolean useZoom) {
double width = project.getWidth().convertToPixels();
double height = project.getHeight().convertToPixels();
if (useZoom) {
width *= zoomLevel;
height *= zoomLevel;
} else {
width /= Constants.PIXEL_SIZE;
height /= Constants.PIXEL_SIZE;
}
return new Dimension((int) width, (int) height);
}
public void fireZoomChanged() {
messageDispatcher.dispatchMessage(EventType.ZOOM_CHANGED, zoomLevel);
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
public Theme getTheme() {
return theme;
}
public void setTheme(Theme theme) {
this.theme = theme;
ConfigurationManager.getInstance().writeValue(IPlugInPort.THEME_KEY, theme);
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
}
public static final String ZOOM_KEY = "zoom";
public static String DEBUG_COMPONENT_AREAS = "org.diylc.debugComponentAreas";
public static String DEBUG_CONTINUITY_AREAS = "org.diylc.debugContinuityAreas";
public static Color CONTROL_POINT_COLOR = Color.blue;
public static Color SELECTED_CONTROL_POINT_COLOR = Color.green;
private Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
Constants.DEFAULT_THEME);
// Keeps Area object of each drawn component.
private Map<IDIYComponent<?>, ComponentArea> componentAreaMap;
// Maps components to the last state they are drawn in. Also, used to
// determine which components are invalidated when they are not in the map.
private Map<IDIYComponent<?>, ComponentState> lastDrawnStateMap;
private Area continuityArea;
private Composite slotComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
private Composite lockedComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
private List<IDIYComponent<?>> failedComponents = new ArrayList<IDIYComponent<?>>();
private double zoomLevel = 1d;// ConfigurationManager.getInstance().readDouble(ZOOM_KEY,
// 1d);
private MessageDispatcher<EventType> messageDispatcher;
private boolean debugComponentAreas;
private boolean debugContinuityAreas;
public DrawingManager(MessageDispatcher<EventType> messageDispatcher) {
super();
this.messageDispatcher = messageDispatcher;
componentAreaMap = new HashMap<IDIYComponent<?>, ComponentArea>();
lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS);
debugComponentAreas = debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true");
String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS);
debugContinuityAreas = debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true");
}
/**
* Paints the project onto the canvas and returns the list of components that failed to draw.
*
* @param g2d
* @param project
* @param drawOptions
* @param filter
* @param selectionRect
* @param selectedComponents
* @param lockedComponents
* @param groupedComponents
* @param controlPointSlot
* @param componentSlot
* @param dragInProgress
* @return
*/
public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project, Set<DrawOption> drawOptions,
IComponentFiler filter, Rectangle selectionRect, Collection<IDIYComponent<?>> selectedComponents,
Set<IDIYComponent<?>> lockedComponents, Set<IDIYComponent<?>> groupedComponents, List<Point> controlPointSlot,
List<IDIYComponent<?>> componentSlot, boolean dragInProgress) {
failedComponents.clear();
if (project == null) {
return failedComponents;
}
double zoom = 1d;
if (drawOptions.contains(DrawOption.ZOOM)) {
zoom = zoomLevel;
} else {
zoom = 1 / Constants.PIXEL_SIZE;
}
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
if (drawOptions.contains(DrawOption.ANTIALIASING)) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
// RenderingHints.VALUE_STROKE_PURE);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
}
// AffineTransform initialTx = g2d.getTransform();
Dimension d = getCanvasDimensions(project, zoom, true);
g2dWrapper.setColor(theme.getBgColor());
g2dWrapper.fillRect(0, 0, d.width, d.height);
g2d.clip(new Rectangle(new Point(0, 0), d));
GridType gridType = GridType.LINES;
if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) {
double zoomStep = project.getGridSpacing().convertToPixels() * zoom;
if (gridType == GridType.CROSSHAIR) {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {
(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4));
} else if (gridType == GridType.DOT) {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {1f,
(float) zoomStep - 1}, 0f));
}
g2dWrapper.setColor(theme.getGridColor());
for (double i = zoomStep; i < d.width; i += zoomStep) {
g2dWrapper.drawLine((int) i, 0, (int) i, d.height - 1);
}
for (double j = zoomStep; j < d.height; j += zoomStep) {
g2dWrapper.drawLine(0, (int) j, d.width - 1, (int) j);
}
}
if (Math.abs(1.0 - zoom) > 1e-4) {
g2dWrapper.scale(zoom, zoom);
}
// Composite mainComposite = g2d.getComposite();
// Composite alphaComposite =
// AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
// g2dWrapper.resetTx();
// componentAreaMap.clear();
for (IDIYComponent<?> component : project.getComponents()) {
// Do not draw the component if it's filtered out.
if (filter != null && !filter.testComponent(component)) {
continue;
}
ComponentState state = ComponentState.NORMAL;
if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) {
if (dragInProgress) {
state = ComponentState.DRAGGING;
} else {
state = ComponentState.SELECTED;
}
}
// Do not track the area if component is not invalidated and was
// drawn in the same state.
boolean trackArea = lastDrawnStateMap.get(component) != state;
synchronized (g2d) {
g2dWrapper.startedDrawingComponent();
if (!trackArea) {
g2dWrapper.stopTracking();
}
// Draw locked components in a new composite.
if (lockedComponents.contains(component)) {
g2d.setComposite(lockedComposite);
}
// Draw the component through the g2dWrapper.
try {
component.draw(g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
} catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e);
failedComponents.add(component);
}
ComponentArea area = g2dWrapper.finishedDrawingComponent();
if (trackArea && area != null && !area.getOutlineArea().isEmpty()) {
componentAreaMap.put(component, area);
lastDrawnStateMap.put(component, state);
}
}
}
// Draw control points.
if (drawOptions.contains(DrawOption.CONTROL_POINTS)) {
// Draw unselected points first to make sure they are below.
if (dragInProgress || drawOptions.contains(DrawOption.OUTLINE_MODE)) {
for (IDIYComponent<?> component : project.getComponents()) {
for (int i = 0; i < component.getControlPointCount(); i++) {
VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i);
if ((groupedComponents.contains(component)
&& (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) || (!groupedComponents
.contains(component) && !selectedComponents.contains(component) && component
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) {
g2dWrapper.setColor(CONTROL_POINT_COLOR);
Point controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE - 2;
g2dWrapper.fillOval(controlPoint.x - pointSize / 2, controlPoint.y - pointSize / 2, pointSize, pointSize);
}
}
}
}
// Then draw the selected ones.
for (IDIYComponent<?> component : selectedComponents) {
for (int i = 0; i < component.getControlPointCount(); i++) {
if (!groupedComponents.contains(component)
&& (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) {
Point controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE;
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval(controlPoint.x - pointSize / 2, controlPoint.y - pointSize / 2, pointSize, pointSize);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval(controlPoint.x - CONTROL_POINT_SIZE / 2 + 1, controlPoint.y - CONTROL_POINT_SIZE / 2
+ 1, CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2);
}
}
}
}
// Draw component slot in a separate composite.
if (componentSlot != null) {
g2dWrapper.startedDrawingComponent();
g2dWrapper.setComposite(slotComposite);
for (IDIYComponent<?> component : componentSlot) {
try {
component.draw(g2dWrapper, ComponentState.NORMAL, drawOptions.contains(DrawOption.OUTLINE_MODE), project,
g2dWrapper);
} catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e);
failedComponents.add(component);
}
}
g2dWrapper.finishedDrawingComponent();
}
// Draw control points of the component in the slot.
if (controlPointSlot != null) {
for (Point point : controlPointSlot) {
if (point != null) {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval(point.x - CONTROL_POINT_SIZE / 2, point.y - CONTROL_POINT_SIZE / 2, CONTROL_POINT_SIZE,
CONTROL_POINT_SIZE);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval(point.x - CONTROL_POINT_SIZE / 2 + 1, point.y - CONTROL_POINT_SIZE / 2 + 1,
CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2);
}
}
}
// Go back to the original transformation and zoom in to draw the
// selection rectangle and other similar elements.
// g2d.setTransform(initialTx);
// if ((drawOptions.contains(DrawOption.ZOOM)) && (Math.abs(1.0 -
// zoomLevel) > 1e-4)) {
// g2d.scale(zoomLevel, zoomLevel);
// }
// At the end draw selection rectangle if needed.
if (drawOptions.contains(DrawOption.SELECTION) && (selectionRect != null)) {
g2d.setColor(Color.white);
g2d.draw(selectionRect);
g2d.setColor(Color.black);
g2d.setStroke(Constants.DASHED_STROKE);
g2d.draw(selectionRect);
}
// Draw component area for test
if (debugComponentAreas) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.setColor(Color.red);
for (ComponentArea area : componentAreaMap.values()) {
g2d.draw(area.getOutlineArea());
}
}
// Draw continuity area for test
if (debugContinuityAreas) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
g2d.setColor(Color.green);
for (ComponentArea area : componentAreaMap.values()) {
for (Area a : area.getContinuityPositiveAreas())
g2d.draw(a);
}
g2d.setColor(Color.blue);
for (ComponentArea area : componentAreaMap.values()) {
for (Area a : area.getContinuityNegativeAreas())
g2d.draw(a);
}
}
if (continuityArea != null && (ConfigurationManager.getInstance().readBoolean(IPlugInPort.HIGHLIGHT_CONTINUITY_AREA, false)))
{
Composite oldComposite = g2d.getComposite();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.setColor(Color.green);
g2d.fill(continuityArea);
g2d.setComposite(oldComposite);
}
return failedComponents;
}
public double getZoomLevel() {
return zoomLevel;
}
public void setZoomLevel(double zoomLevel) {
this.zoomLevel = zoomLevel;
fireZoomChanged();
// ConfigurationManager.getInstance().writeValue(ZOOM_KEY, zoomLevel);
}
public void invalidateComponent(IDIYComponent<?> component) {
componentAreaMap.remove(component);
lastDrawnStateMap.remove(component);
}
public ComponentArea getComponentArea(IDIYComponent<?> component) {
return componentAreaMap.get(component);
}
public void clearComponentAreaMap() {
componentAreaMap.clear();
lastDrawnStateMap.clear();
}
public List<IDIYComponent<?>> findComponentsAt(Point point, Project project) {
List<IDIYComponent<?>> components = new ArrayList<IDIYComponent<?>>();
for (int i = 0; i < project.getComponents().size(); i++) {
ComponentArea area = componentAreaMap.get(project.getComponents().get(i));
if (area != null && area.getOutlineArea().contains(point)) {
components.add(0, project.getComponents().get(i));
}
}
return components;
}
public Dimension getCanvasDimensions(Project project, Double zoomLevel, boolean useZoom) {
double width = project.getWidth().convertToPixels();
double height = project.getHeight().convertToPixels();
if (useZoom) {
width *= zoomLevel;
height *= zoomLevel;
} else {
width /= Constants.PIXEL_SIZE;
height /= Constants.PIXEL_SIZE;
}
return new Dimension((int) width, (int) height);
}
public void fireZoomChanged() {
messageDispatcher.dispatchMessage(EventType.ZOOM_CHANGED, zoomLevel);
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
public Theme getTheme() {
return theme;
}
public void setTheme(Theme theme) {
this.theme = theme;
ConfigurationManager.getInstance().writeValue(IPlugInPort.THEME_KEY, theme);
messageDispatcher.dispatchMessage(EventType.REPAINT);
}
@SuppressWarnings("unchecked")
public void findContinuityAreaAtPoint(Project project, Point p) {
// Find all individual continuity areas for all components
List<Area> preliminaryAreas = new ArrayList<Area>();
List<Boolean> checkBreakout = new ArrayList<Boolean>();
List<Connection> connections = new ArrayList<Connection>();
for (IDIYComponent<?> c : project.getComponents()) {
ComponentArea a = getComponentArea(c);
ComponentType type =
ComponentProcessor.getInstance().extractComponentTypeFrom((Class<? extends IDIYComponent<?>>) c.getClass());
if (type.isContinuity()) {
connections.add(new Connection(c.getControlPoint(0), c.getControlPoint(c.getControlPointCount() - 1)));
}
if (a == null || a.getOutlineArea() == null)
continue;
if (a.getContinuityPositiveAreas() != null)
for (Area a1 : a.getContinuityPositiveAreas()) {
preliminaryAreas.add(a1);
checkBreakout.add(false);
}
if (a.getContinuityNegativeAreas() != null) {
for (Area na : a.getContinuityNegativeAreas())
for (int i = 0; i < preliminaryAreas.size(); i++) {
Area a1 = preliminaryAreas.get(i);
if (a1.intersects(na.getBounds2D())) {
a1.subtract(na);
checkBreakout.set(i, true);
}
}
}
}
// Check if we need to break some areas out in case they are interrupted
List<Area> areas = new ArrayList<Area>();
for (int i = 0; i < preliminaryAreas.size(); i++) {
Area a = preliminaryAreas.get(i);
if (checkBreakout.get(i))
areas.addAll(tryBreakout(a));
else
areas.add(a);
}
crunchAreas(areas, connections);
for (Area a : areas) {
if (a.contains(p)) {
continuityArea = a;
return;
}
}
continuityArea = null;
}
private boolean crunchAreas(List<Area> areas, List<Connection> connections) {
boolean isChanged = false;
List<Area> newAreas = new ArrayList<Area>();
List<Boolean> consumed = new ArrayList<Boolean>();
for (int i = 0; i < areas.size(); i++) {
consumed.add(false);
}
for (int i = 0; i < areas.size(); i++) {
for (int j = i + 1; j < areas.size(); j++) {
if (consumed.get(j))
continue;
Area a1 = areas.get(i);
Area a2 = areas.get(j);
Area intersection = new Area(a1);
intersection.intersect(a2);
// if the two areas intersect, make a union and consume the second area
if (!intersection.isEmpty()) {
a1.add(a2);
consumed.set(j, true);
} else { // maybe there's a connection between them
for (Connection p : connections) {
if ((a1.contains(p.pointA) && a2.contains(p.pointB)) || (a1.contains(p.pointB) && a2.contains(p.pointA))) {
a1.add(a2);
consumed.set(j, true);
break;
}
}
}
}
}
for (int i = 0; i < areas.size(); i++)
if (!consumed.get(i))
newAreas.add(areas.get(i));
else
isChanged = true;
if (isChanged) {
areas.clear();
areas.addAll(newAreas);
crunchAreas(areas, connections);
}
return isChanged;
}
private List<Area> tryBreakout(Area a) {
List<Area> toReturn = new ArrayList<Area>();
Path2D p = null;
PathIterator pathIterator = a.getPathIterator(null);
while (!pathIterator.isDone()) {
double[] coord = new double[6];
int type = pathIterator.currentSegment(coord);
switch (type) {
case PathIterator.SEG_MOVETO:
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
p = new Path2D.Double();
p.moveTo(coord[0], coord[1]);
break;
case PathIterator.SEG_LINETO:
p.lineTo(coord[0], coord[1]);
break;
case PathIterator.SEG_CUBICTO:
p.curveTo(coord[0], coord[1], coord[2], coord[3], coord[4], coord[5]);
break;
case PathIterator.SEG_QUADTO:
p.quadTo(coord[0], coord[1], coord[2], coord[3]);
break;
}
pathIterator.next();
}
if (p != null) {
Area partArea = new Area(p);
toReturn.add(partArea);
}
return toReturn;
}
class Connection {
private Point pointA;
private Point pointB;
public Connection(Point pointA, Point pointB) {
super();
this.pointA = pointA;
this.pointB = pointB;
}
public Point getPointA() {
return pointA;
}
public Point getPointB() {
return pointB;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -63,7 +63,7 @@ public class InstantiationManager {
private static final ComponentType clipboardType = new ComponentType("Clipboard contents",
"Components from the clipboard", CreationMethod.SINGLE_CLICK, "Multi", "", "", null, null, 0, false, false, null,
false, null, KeywordPolicy.NEVER_SHOW, null);
false, null, KeywordPolicy.NEVER_SHOW, null, false);
public InstantiationManager() {}
@@ -21,63 +21,63 @@
*/
package org.diylc.presenter;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.DnDConstants;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.miscutils.Utils;
import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.appframework.update.Version;
import org.diylc.appframework.update.VersionNumber;
import org.diylc.common.ComponentType;
import org.diylc.common.DrawOption;
import org.diylc.common.EventType;
import org.diylc.common.IComponentFiler;
import org.diylc.common.IComponentTransformer;
import org.diylc.common.IKeyProcessor;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.ExpansionMode;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IView;
import org.diylc.core.Project;
import org.diylc.core.Template;
import org.diylc.core.Theme;
import org.diylc.core.annotations.IAutoCreator;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.DnDConstants;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.miscutils.Utils;
import org.diylc.appframework.simplemq.MessageDispatcher;
import org.diylc.appframework.update.Version;
import org.diylc.appframework.update.VersionNumber;
import org.diylc.common.ComponentType;
import org.diylc.common.DrawOption;
import org.diylc.common.EventType;
import org.diylc.common.IComponentFiler;
import org.diylc.common.IComponentTransformer;
import org.diylc.common.IKeyProcessor;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.ExpansionMode;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IView;
import org.diylc.core.Project;
import org.diylc.core.Template;
import org.diylc.core.Theme;
import org.diylc.core.annotations.IAutoCreator;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* The main presenter class, contains core app logic and drawing routines.
@@ -132,7 +132,7 @@ public class Presenter implements IPlugInPort {
private ProjectFileManager projectFileManager;
private InstantiationManager instantiationManager;
private Rectangle selectionRect;
private Rectangle selectionRect;
private final IView view;
@@ -148,7 +148,7 @@ public class Presenter implements IPlugInPort {
private Point previousDragPoint = null;
private Project preDragProject = null;
private int dragAction;
private Point previousScaledPoint;
private Point previousScaledPoint;
public Presenter(IView view) {
super();
@@ -212,8 +212,8 @@ public class Presenter implements IPlugInPort {
}
for (IDIYComponent<?> component : currentProject.getComponents()) {
if (!isComponentLocked(component) && isComponentVisible(component)) {
Area area = drawingManager.getComponentArea(component);
if (area != null && area.contains(scaledPoint)) {
ComponentArea area = drawingManager.getComponentArea(component);
if (area != null && area.getOutlineArea() != null && area.getOutlineArea().contains(scaledPoint)) {
return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
}
}
@@ -599,7 +599,10 @@ public class Presenter implements IPlugInPort {
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, oldProject, currentProject.clone(), "Add "
+ componentTypeSlot.getName());
projectFileManager.notifyFileChange();
}
}
} else if (ConfigurationManager.getInstance().readBoolean(HIGHLIGHT_CONTINUITY_AREA, false)) {
drawingManager.findContinuityAreaAtPoint(currentProject, scaledPoint);
messageDispatcher.dispatchMessage(EventType.REPAINT);
} else {
List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>(selectedComponents);
List<IDIYComponent<?>> components = findComponentsAtScaled(scaledPoint);
@@ -867,11 +870,11 @@ public class Presenter implements IPlugInPort {
Area totalArea = new Area();
for (IDIYComponent<?> c : selectedComponents) {
Area compArea = drawingManager.getComponentArea(c);
if (compArea != null)
totalArea.add(compArea);
ComponentArea compArea = drawingManager.getComponentArea(c);
if (compArea != null && compArea.getOutlineArea() != null)
totalArea.add(compArea.getOutlineArea());
else
LOG.info("Area is null");
LOG.debug("Area is null");
}
if (drawingManager.getZoomLevel() != 1 && applyZoom) {
totalArea
@@ -1397,8 +1400,8 @@ public class Presenter implements IPlugInPort {
List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>();
for (IDIYComponent<?> component : currentProject.getComponents()) {
if (!isComponentLocked(component) && isComponentVisible(component)) {
Area area = drawingManager.getComponentArea(component);
if ((area != null) && (selectionRect != null) && area.intersects(selectionRect)) {
ComponentArea area = drawingManager.getComponentArea(component);
if ((area != null && area.getOutlineArea() != null) && (selectionRect != null) && area.getOutlineArea().intersects(selectionRect)) {
newSelection.addAll(findAllGroupedComponents(component));
}
}
@@ -1801,16 +1804,16 @@ public class Presenter implements IPlugInPort {
for (IDIYComponent<?> component : getCurrentProject().getComponents()) {
// Skip already selected components or ones that cannot be stuck to
// other components.
Area area = drawingManager.getComponentArea(component);
if (newSelection.contains(component) || !component.isControlPointSticky(0) || area == null)
ComponentArea area = drawingManager.getComponentArea(component);
if (newSelection.contains(component) || !component.isControlPointSticky(0) || area == null || area.getOutlineArea() == null)
continue;
boolean matches = false;
for (IDIYComponent<?> selectedComponent : this.selectedComponents) {
Area selectedArea = drawingManager.getComponentArea(selectedComponent);
if (selectedArea == null)
ComponentArea selectedArea = drawingManager.getComponentArea(selectedComponent);
if (selectedArea == null || selectedArea.getOutlineArea() == null)
continue;
Area intersection = new Area(area);
intersection.intersect(selectedArea);
Area intersection = new Area(area.getOutlineArea());
intersection.intersect(selectedArea.getOutlineArea());
if (!intersection.isEmpty()) {
matches = true;
break;