diff --git a/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java b/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java index 26f6653a..275b8ffe 100644 --- a/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java +++ b/diylc/diylc-core/src/org/diylc/components/AbstractComponent.java @@ -251,5 +251,10 @@ public abstract class AbstractComponent implements IDIYComponent { @Override public boolean canPointMoveFreely(int pointIndex) { return true; + } + + @Override + public Rectangle2D getCachingBounds() { + return null; } } diff --git a/diylc/diylc-core/src/org/diylc/components/AbstractCurvedComponent.java b/diylc/diylc-core/src/org/diylc/components/AbstractCurvedComponent.java index c54b339c..a80a001c 100644 --- a/diylc/diylc-core/src/org/diylc/components/AbstractCurvedComponent.java +++ b/diylc/diylc-core/src/org/diylc/components/AbstractCurvedComponent.java @@ -25,6 +25,7 @@ import java.awt.Point; import java.awt.geom.CubicCurve2D; import java.awt.geom.Path2D; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import org.apache.log4j.Logger; import org.diylc.appframework.miscutils.ConfigurationManager; @@ -360,6 +361,28 @@ public abstract class AbstractCurvedComponent extends AbstractTransparentComp return null; } } + + @Override + public Rectangle2D getCachingBounds() { + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + int margin = 50; // hard coded, replace with something meaningful + for (int i = 0; i < getControlPointCount(); i++) { + Point p = getControlPoint(i); + if (p.x < minX) + minX = p.x; + if (p.x > maxX) + maxX = p.x; + if (p.y < minY) + minY = p.y; + if (p.y > maxY) + maxY = p.y; + } + + return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin); + } public enum PointCount { TWO(2), THREE(3), FOUR(4), FIVE(5), SEVEN(7); diff --git a/diylc/diylc-core/src/org/diylc/core/IDIYComponent.java b/diylc/diylc-core/src/org/diylc/core/IDIYComponent.java index 0d5c8681..7bb4b5a3 100644 --- a/diylc/diylc-core/src/org/diylc/core/IDIYComponent.java +++ b/diylc/diylc-core/src/org/diylc/core/IDIYComponent.java @@ -22,7 +22,8 @@ package org.diylc.core; import java.awt.Graphics2D; -import java.awt.Point; +import java.awt.Point; +import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.diylc.core.annotations.ComponentDescriptor; @@ -189,5 +190,11 @@ public interface IDIYComponent extends Serializable, Cloneable { * @param other * @return */ - boolean equalsTo(IDIYComponent other); + boolean equalsTo(IDIYComponent other); + + /** + * Used only if {@link ComponentDescriptor#enableCache()} is set to true. + * @return + */ + Rectangle2D getCachingBounds(); } diff --git a/diylc/diylc-core/src/org/diylc/presenter/Counter.java b/diylc/diylc-core/src/org/diylc/presenter/Counter.java new file mode 100644 index 00000000..f4a6fd46 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/presenter/Counter.java @@ -0,0 +1,44 @@ +/* + * + * 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 + * . + */ +package org.diylc.presenter; + +import java.util.concurrent.TimeUnit; + +public class Counter { + + private int count; + private long nanotime; + + public int getCount() { + return count; + } + + public long getNanoTime() { + return nanotime; + } + + public void add(long nanotime) { + count++; + this.nanotime += nanotime; + } + + @Override + public String toString() { + return String.format("%dx %dms", count, TimeUnit.MILLISECONDS.convert(nanotime, TimeUnit.NANOSECONDS)); + } +} diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java index f8b242d4..ad1ae80e 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java +++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java @@ -19,15 +19,19 @@ package org.diylc.presenter; import java.awt.Graphics2D; import java.awt.Point; +import java.awt.geom.Area; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; import org.apache.log4j.Logger; import org.diylc.common.ComponentType; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; import org.diylc.core.Project; +import org.diylc.utils.Pair; /** * A layer that injects itself between the provided {@link G2DWrapper} instance and the underlying @@ -39,8 +43,9 @@ import org.diylc.core.Project; */ public class DrawingCache { - private Map, CacheValue> imageCache = - new HashMap, CacheValue>(); + private Map, CacheValue> imageCache = new HashMap, CacheValue>(); + + private Map> renderStatsByType = new HashMap>(); private static final Logger LOG = Logger.getLogger(DrawingCache.class); @@ -55,7 +60,6 @@ public class DrawingCache { if (type.getEnableCache()) { // if we need to apply caching Point firstPoint = component.getControlPoint(0); - Point lastPoint = component.getControlPoint(component.getControlPointCount() - 1); // cache hit! CacheValue value = null; @@ -66,14 +70,22 @@ public class DrawingCache { if (value == null || !value.getComponent().equalsTo(component) || value.getState() != componentState || value.getZoom() != zoom) { LOG.trace("Rendering " + component.getName() + " for cache."); - int width = (int) (Math.abs(firstPoint.x - lastPoint.x) * zoom) + 2; - int height = (int) (Math.abs(firstPoint.y - lastPoint.y) * zoom) + 2; + Rectangle2D rect = component.getCachingBounds(); + int width = (int)Math.round(rect.getWidth() * zoom); + int height = (int)Math.round(rect.getHeight() * zoom); + // calculate the position of the first point relative to the caching bounds + int dx = (int) (firstPoint.x - rect.getX()); + int dy = (int) (firstPoint.y - rect.getY()); + + // create image BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // create graphics Graphics2D cg2d = image.createGraphics(); - // copy over rendering settings + // copy over rendering settings so our cached image has the same features cg2d.setRenderingHints(g2d.getRenderingHints()); + // set clip bounds to the whole image cg2d.setClip(0, 0, width, height); // apply zoom @@ -81,24 +93,41 @@ public class DrawingCache { cg2d.scale(zoom, zoom); } // translate to make the top-left corner of the component match (0, 0) - cg2d.translate(-firstPoint.x, -firstPoint.y); + cg2d.translate(-firstPoint.x + dx, -firstPoint.y + dy); // wrap the graphics so we can track G2DWrapper wrapper = new G2DWrapper(cg2d, zoom); + // initialize wrapper wrapper.startedDrawingComponent(); if (!trackArea) { wrapper.stopTracking(); } + + long componentStart = System.nanoTime(); // now draw the component to the buffer image component.draw(wrapper, componentState, outlineMode, project, wrapper); + + long componentEnd = System.nanoTime(); + + Pair stats; + String key = component.getClass().getCanonicalName().replace("org.diylc.components.", ""); + if (renderStatsByType.containsKey(key)) { + stats = renderStatsByType.get(key); + } else { + stats = new Pair(new Counter(), new Counter()); + renderStatsByType.put(key, stats); + } + stats.getFirst().add(componentEnd - componentStart); + // finalize wrapper wrapper.finishedDrawingComponent(); cg2d.dispose(); -// File outputfile = new File("d:\\image_" + component.getName() + ".png"); + // output cached drawings to separate files +// File outputfile = new File("d:\\tmp\\image_" + component.getName() + ".png"); // try { // ImageIO.write(image, "png", outputfile); // } catch (IOException e) { @@ -106,10 +135,13 @@ public class DrawingCache { // e.printStackTrace(); // } - value = new CacheValue(component, image, wrapper, componentState, zoom); - + // add to the cache + value = new CacheValue(component, image, wrapper.getCurrentArea(), wrapper.getContinuityPositiveAreas(), + wrapper.getContinuityNegativeAreas(), componentState, zoom, dx, dy); imageCache.put(component, value); } + + long componentStart = System.nanoTime(); // temporarily scale the graphic to compensate for the zoom, since we are rendering an image with the zoom appled if (Math.abs(1.0 - zoom) > 1e-4) { @@ -117,21 +149,45 @@ public class DrawingCache { } try { // draw cached image - g2d.getCanvasGraphics().drawImage(value.getImage(), (int) (firstPoint.x * zoom), - (int) (firstPoint.y * zoom), null); + g2d.getCanvasGraphics().drawImage(value.getImage(), (int) ((firstPoint.x - value.getDx()) * zoom), + (int) ((firstPoint.y - value.getDy())* zoom), null); } finally { if (Math.abs(1.0 - zoom) > 1e-4) { g2d.getCanvasGraphics().scale(zoom, zoom); } } + + long componentEnd = System.nanoTime(); + + Pair stats; + String key = component.getClass().getCanonicalName().replace("org.diylc.components.", ""); + if (renderStatsByType.containsKey(key)) { + stats = renderStatsByType.get(key); + } else { + stats = new Pair(new Counter(), new Counter()); + renderStatsByType.put(key, stats); + } + stats.getSecond().add(componentEnd - componentStart); // copy over tracking area from the cache - g2d.merge(value.getWrapper()); + g2d.merge(value.getCurrentArea(), value.getContinuityPositiveAreas(), value.getContinuityNegativeAreas()); } else { // no caching, just draw as usual component.draw(g2d, componentState, outlineMode, project, g2d); } } + + public void logStats() { + int totalSizeMB = imageCache.values().stream().map(x -> x.image.getData().getDataBuffer().getSize()).reduce(0, Integer::sum) * 4 / 1024 / 1024; // 4 bytes per pixel + LOG.debug(String.format("Render cache contains %d elements, approx size is %d MB", imageCache.size(), totalSizeMB)); + + String mapAsString = renderStatsByType.entrySet().stream().sorted((e1, e2) -> -Long.compare(e1.getValue().getFirst().getNanoTime() + e1.getValue().getSecond().getNanoTime(), + e2.getValue().getFirst().getNanoTime() + e2.getValue().getSecond().getNanoTime())) + .map(e -> e.getKey() + ": render: " + e.getValue().getFirst() + ", paste: " + e.getValue().getSecond()) + .collect(Collectors.joining("; ", "{", "}")); + + LOG.debug("Cache render distribution stats: " + mapAsString); + } public void clear() { imageCache.clear(); @@ -140,12 +196,17 @@ public class DrawingCache { private class CacheValue { IDIYComponent component; BufferedImage image; - G2DWrapper wrapper; + Area currentArea; + Map continuityPositiveAreas; + Map continuityNegativeAreas; ComponentState state; double zoom; + int dx; + int dy; - public CacheValue(IDIYComponent component, BufferedImage image, G2DWrapper wrapper, - ComponentState state, double zoom) { + public CacheValue(IDIYComponent component, BufferedImage image, Area currentArea, + Map continuityPositiveAreas, Map continuityNegativeAreas, + ComponentState state, double zoom, int dx, int dy) { super(); // take a copy of the component, so we can check if it changed in the meantime try { @@ -153,9 +214,13 @@ public class DrawingCache { } catch (CloneNotSupportedException e) { } this.image = image; - this.wrapper = wrapper; + this.currentArea = currentArea; + this.continuityPositiveAreas = continuityPositiveAreas; + this.continuityNegativeAreas = continuityNegativeAreas; this.state = state; this.zoom = zoom; + this.dx = dx; + this.dy = dy; } public IDIYComponent getComponent() { @@ -166,8 +231,16 @@ public class DrawingCache { return image; } - public G2DWrapper getWrapper() { - return wrapper; + public Area getCurrentArea() { + return currentArea; + } + + public Map getContinuityPositiveAreas() { + return continuityPositiveAreas; + } + + public Map getContinuityNegativeAreas() { + return continuityNegativeAreas; } public ComponentState getState() { @@ -177,6 +250,14 @@ public class DrawingCache { public double getZoom() { return zoom; } + + public int getDx() { + return dx; + } + + public int getDy() { + return dy; + } @Override public String toString() { diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java index 1f2fad92..fc5c3f91 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java @@ -58,7 +58,6 @@ import org.diylc.core.Project; import org.diylc.core.Theme; import org.diylc.core.VisibilityPolicy; import org.diylc.utils.Constants; -import org.diylc.utils.Pair; /** * Utility that deals with painting {@link Project} on the {@link Graphics2D} and keeps areas taken @@ -109,7 +108,7 @@ public class DrawingManager { private boolean debugContinuityAreas; // rendering stats - private Map> renderStatsByType = new HashMap>(); + private Map renderStatsByType = new HashMap(); private long lastStatsReportedTime = System.currentTimeMillis(); private long statReportFrequencyMs = 1000 * 60; @@ -305,26 +304,15 @@ public class DrawingManager { } finally { // record render stats long componentEnd = System.nanoTime(); - Pair stats = null; - String key = component.getClass().getCanonicalName(); + Counter stats = null; + String key = component.getClass().getCanonicalName().replace("org.diylc.components.", ""); if (renderStatsByType.containsKey(key)) { - stats = renderStatsByType.get(key); - stats.setFirst(stats.getFirst() + 1); - stats.setSecond(stats.getSecond() + (componentEnd - componentStart)); + stats = renderStatsByType.get(key); } else { - stats = new Pair(1, componentEnd - componentStart); + stats = new Counter(); renderStatsByType.put(key, stats); } - - // log render time stats periodically - if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { - lastStatsReportedTime = System.currentTimeMillis(); - String mapAsString = renderStatsByType.entrySet().stream().sorted((e1, e2) -> -e1.getValue().getSecond().compareTo(e2.getValue().getSecond())) - .map(e -> e.getKey() + "=" + e.getValue().getFirst() + "x " + - TimeUnit.MILLISECONDS.convert( e.getValue().getSecond(), TimeUnit.NANOSECONDS) + "ms ") - .collect(Collectors.joining(", ", "{", "}")); - LOG.debug("Render stats: " + mapAsString); - } + stats.add(componentEnd - componentStart); } ComponentArea area = g2dWrapper.finishedDrawingComponent(); if (trackArea && area != null && !area.getOutlineArea().isEmpty()) { @@ -469,9 +457,23 @@ public class DrawingManager { long ms = TimeUnit.MILLISECONDS.convert(end - start, TimeUnit.NANOSECONDS); LOG.trace(String.format("Render time = %d ms", ms)); + + logStats(); return failedComponents; } + + public void logStats() { + // log render time stats periodically + if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { + lastStatsReportedTime = System.currentTimeMillis(); + String mapAsString = renderStatsByType.entrySet().stream().sorted((e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) + .map(e -> e.toString()) + .collect(Collectors.joining("; ", "{", "}")); + LOG.debug("Render stats: " + mapAsString); + DrawingCache.Instance.logStats(); + } + } public double getZoomLevel() { return zoomLevel; diff --git a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java index 16f9d9ec..e892f3f0 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java +++ b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java @@ -162,10 +162,26 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver { return new ComponentArea(currentArea, continuityPositiveAreas.values(), continuityNegativeAreas.values()); } - public void merge(G2DWrapper wrapper) { - currentArea = wrapper.currentArea; - continuityPositiveAreas = wrapper.continuityPositiveAreas; - continuityNegativeAreas = wrapper.continuityNegativeAreas; + // start - used for caching + + public Area getCurrentArea() { + return currentArea; + } + + public Map getContinuityPositiveAreas() { + return continuityPositiveAreas; + } + + public Map getContinuityNegativeAreas() { + return continuityNegativeAreas; + } + + // end - used for caching + + public void merge(Area currentArea, Map continuityPositiveAreas, Map continuityNegativeAreas) { + this.currentArea = currentArea; + this.continuityPositiveAreas = continuityPositiveAreas; + this.continuityNegativeAreas = continuityNegativeAreas; } @Override diff --git a/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java b/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java index 70b8d4f5..cf380931 100644 --- a/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java +++ b/diylc/diylc-library/src/org/diylc/components/AbstractLeadedComponent.java @@ -668,6 +668,28 @@ public abstract class AbstractLeadedComponent extends AbstractTransparentComp return null; return Integer.toString(index + 1); } + + @Override + public Rectangle2D getCachingBounds() { + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + int margin = (int) Math.max(Math.max(getWidth().convertToPixels(), getLength().convertToPixels()), getLeadThickness()); + for (int i = 0; i < getControlPointCount(); i++) { + Point p = getControlPoint(i); + if (p.x < minX) + minX = p.x; + if (p.x > maxX) + maxX = p.x; + if (p.y < minY) + minY = p.y; + if (p.y > maxY) + maxY = p.y; + } + + return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin); + } public enum LabelOriantation { Directional, Horizontal diff --git a/diylc/diylc-library/src/org/diylc/components/boards/AbstractBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/AbstractBoard.java index 33ec9943..c328f71f 100644 --- a/diylc/diylc-library/src/org/diylc/components/boards/AbstractBoard.java +++ b/diylc/diylc-library/src/org/diylc/components/boards/AbstractBoard.java @@ -26,6 +26,7 @@ import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Point; +import java.awt.geom.Rectangle2D; import org.diylc.awt.StringUtils; import org.diylc.common.HorizontalAlignment; @@ -332,6 +333,12 @@ public abstract class AbstractBoard extends AbstractTransparentComponent public void setMode(BoardSizingMode mode) { this.mode = mode; } + + @Override + public Rectangle2D getCachingBounds() { + Point finalSecondPoint = getFinalSecondPoint(); + return new Rectangle2D.Double(firstPoint.x, firstPoint.y, finalSecondPoint.x - firstPoint.x + 1, finalSecondPoint.y - firstPoint.y + 1); + } public static enum CoordinateType { Letters, Numbers diff --git a/diylc/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java b/diylc/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java index 8394aaad..7333fdaa 100644 --- a/diylc/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java +++ b/diylc/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java @@ -39,7 +39,8 @@ import org.diylc.core.measures.SizeUnit; @ComponentDescriptor(name = "Curved Trace", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "Trace", description = "Curved copper trace with two control points", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, - keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class) + keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "PCB", transformer = SimpleComponentTransformer.class, + enableCache = true) public class CurvedTrace extends AbstractCurvedComponent { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-library/src/org/diylc/components/connectivity/HookupWire.java b/diylc/diylc-library/src/org/diylc/components/connectivity/HookupWire.java index 0c92c859..fb6dbab0 100644 --- a/diylc/diylc-library/src/org/diylc/components/connectivity/HookupWire.java +++ b/diylc/diylc-library/src/org/diylc/components/connectivity/HookupWire.java @@ -39,7 +39,7 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "Hookup Wire", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "W", description = "Flexible wire with two control points", zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, - transformer = SimpleComponentTransformer.class) + transformer = SimpleComponentTransformer.class, enableCache = true) public class HookupWire extends AbstractCurvedComponent implements IContinuity { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-library/src/org/diylc/components/connectivity/TwistedWire.java b/diylc/diylc-library/src/org/diylc/components/connectivity/TwistedWire.java index 959c4479..d5f310a9 100644 --- a/diylc/diylc-library/src/org/diylc/components/connectivity/TwistedWire.java +++ b/diylc/diylc-library/src/org/diylc/components/connectivity/TwistedWire.java @@ -48,7 +48,7 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "Twisted Leads", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "W", description = "A pair of flexible leads twisted tighly together", zOrder = IDIYComponent.COMPONENT, flexibleZOrder = true, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false, - transformer = SimpleComponentTransformer.class) + transformer = SimpleComponentTransformer.class, enableCache = true) public class TwistedWire extends AbstractCurvedComponent implements IContinuity { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java b/diylc/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java index 7bd878d5..c908fd2d 100644 --- a/diylc/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java +++ b/diylc/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java @@ -48,7 +48,7 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "Electrolytic Capacitor (Axial)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Axial electrolytic capacitor, similar to Sprague Atom, F&T, etc", zOrder = IDIYComponent.COMPONENT, - transformer = SimpleComponentTransformer.class) + transformer = SimpleComponentTransformer.class, enableCache = true) public class AxialElectrolyticCapacitor extends AbstractLeadedComponent { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-library/src/org/diylc/components/passive/Resistor.java b/diylc/diylc-library/src/org/diylc/components/passive/Resistor.java index 1c6de734..d79bf85f 100644 --- a/diylc/diylc-library/src/org/diylc/components/passive/Resistor.java +++ b/diylc/diylc-library/src/org/diylc/components/passive/Resistor.java @@ -45,7 +45,7 @@ import org.diylc.core.measures.SizeUnit; @ComponentDescriptor(name = "Resistor", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "R", description = "Resistor layout symbol", - zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class) + zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true) public class Resistor extends AbstractLeadedComponent { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java b/diylc/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java index 0cb6e3ec..621834af 100644 --- a/diylc/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java +++ b/diylc/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java @@ -57,7 +57,8 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "DIP IC", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "IC", description = "Dual-in-line package IC", - zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = DIL_ICTransformer.class) + zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = DIL_ICTransformer.class, + enableCache = true) public class DIL_IC extends AbstractTransparentComponent { private static final long serialVersionUID = 1L; @@ -541,6 +542,28 @@ public class DIL_IC extends AbstractTransparentComponent { @Override public boolean canPointMoveFreely(int pointIndex) { return false; + } + + @Override + public Rectangle2D getCachingBounds() { + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + int margin = 50; + for (int i = 0; i < getControlPointCount(); i++) { + Point p = getControlPoint(i); + if (p.x < minX) + minX = p.x; + if (p.x > maxX) + maxX = p.x; + if (p.y < minY) + minY = p.y; + if (p.y > maxY) + maxY = p.y; + } + + return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin); } public static enum PinCount { diff --git a/diylc/diylc-library/src/org/diylc/components/semiconductors/SIL_IC.java b/diylc/diylc-library/src/org/diylc/components/semiconductors/SIL_IC.java index 8371feb6..c669c30a 100644 --- a/diylc/diylc-library/src/org/diylc/components/semiconductors/SIL_IC.java +++ b/diylc/diylc-library/src/org/diylc/components/semiconductors/SIL_IC.java @@ -56,7 +56,8 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "SIP IC", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "IC", description = "Single-in-line package IC", - zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = SIL_ICTransformer.class) + zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = SIL_ICTransformer.class, + enableCache = true) public class SIL_IC extends AbstractTransparentComponent { private static final long serialVersionUID = 1L; @@ -429,6 +430,28 @@ public class SIL_IC extends AbstractTransparentComponent { public void setIndentColor(Color indentColor) { this.indentColor = indentColor; + } + + @Override + public Rectangle2D getCachingBounds() { + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + int margin = 50; + for (int i = 0; i < getControlPointCount(); i++) { + Point p = getControlPoint(i); + if (p.x < minX) + minX = p.x; + if (p.x > maxX) + maxX = p.x; + if (p.y < minY) + minY = p.y; + if (p.y > maxY) + maxY = p.y; + } + + return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin); } public static enum PinCount { diff --git a/diylc/diylc-swing/DIYLCStarter.launch b/diylc/diylc-swing/DIYLCStarter.launch index a56fd82d..681e6708 100644 --- a/diylc/diylc-swing/DIYLCStarter.launch +++ b/diylc/diylc-swing/DIYLCStarter.launch @@ -11,5 +11,5 @@ - +