diff --git a/diylc/diylc-core/src/org/diylc/common/RoundedPath.java b/diylc/diylc-core/src/org/diylc/common/RoundedPath.java new file mode 100644 index 00000000..6b7da6b2 --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/common/RoundedPath.java @@ -0,0 +1,48 @@ +package org.diylc.common; + +import java.awt.geom.GeneralPath; + +public class RoundedPath { + + private GeneralPath path; + + private double prevX = Double.NaN; + private double prevY = Double.NaN; + private double x = Double.NaN; + private double y = Double.NaN; + + private double radius; + + public RoundedPath(double radius) { + this.radius = radius; + path = new GeneralPath(); + } + + public final void moveTo(double x, double y) { + path.moveTo(x, y); + this.x = x; + this.y = y; + this.prevX = Double.NaN; + this.prevY = Double.NaN; + } + + public final void lineTo(double x, double y) { + if (Double.isNaN(prevX)) { + double theta = Math.atan2(y - this.y, x - this.x); + double r = Math.sqrt((y - this.y) * (y - this.y) + (x - this.x) * (x - this.x)); + path.lineTo(this.x + Math.cos(theta) * (r - radius), this.y + Math.sin(theta) * (r - radius)); + } else { + double theta = Math.atan2(y - this.y, x - this.x); + path.curveTo(this.x, this.y, this.x, this.y, this.x + Math.cos(theta) * radius, this.y + Math.sin(theta) * radius); + } + + this.prevX = this.x; + this.prevY = this.y; + this.x = x; + this.y = y; + } + + public GeneralPath getPath() { + return path; + } +} diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index dcfe4735..427ce079 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java b/diylc/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java index 775c90fb..3a3e149d 100644 --- a/diylc/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java +++ b/diylc/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java @@ -5,12 +5,12 @@ import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Point; -import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import org.diylc.appframework.miscutils.ConfigurationManager; @@ -18,6 +18,7 @@ import org.diylc.common.HorizontalAlignment; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.RoundedPath; import org.diylc.common.VerticalAlignment; import org.diylc.components.AbstractTransparentComponent; import org.diylc.core.ComponentState; @@ -33,10 +34,10 @@ import org.diylc.core.measures.Size; import org.diylc.core.measures.SizeUnit; import org.diylc.utils.Constants; -@ComponentDescriptor(name = "Strat Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic", - description = "Strat-style single coil guitar pickup", stretchable = false, zOrder = IDIYComponent.COMPONENT, - instanceNamePrefix = "PKP", autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG, - keywordTag = "Guitar Wiring Diagram") +@ComponentDescriptor(name = "Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic", + description = "Single coil guitar pickup, both Strat and Tele style", stretchable = false, + zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP", autoEdit = false, + keywordPolicy = KeywordPolicy.SHOW_TAG, keywordTag = "Guitar Wiring Diagram") public class SingleCoilPickup extends AbstractTransparentComponent { private static final long serialVersionUID = 1L; @@ -46,9 +47,19 @@ public class SingleCoilPickup extends AbstractTransparentComponent { private static Color POINT_COLOR = Color.lightGray; private static Size WIDTH = new Size(15.5d, SizeUnit.mm); private static Size LENGTH = new Size(83d, SizeUnit.mm); - private static Size INNER_LENGTH = new Size(70d, SizeUnit.mm); - private static Size LIP_WIDTH = new Size(5d, SizeUnit.mm); - private static Size LIP_LENGTH = new Size(20d, SizeUnit.mm); + private static Size BASE_RADIUS = new Size(0.15d, SizeUnit.in); + + // strat-specific + private static Size STRAT_LIP_WIDTH = new Size(5d, SizeUnit.mm); + private static Size STRAT_LIP_LENGTH = new Size(20d, SizeUnit.mm); + private static Size STRAT_INNER_LENGTH = new Size(70d, SizeUnit.mm); + + // tele-specific + private static Size TELE_BASE_WIDTH = new Size(1.5d, SizeUnit.in); + private static Size TELE_LIP_LENGTH = new Size(1.735d, SizeUnit.in); + private static Size TELE_LENGTH = new Size(2.87d, SizeUnit.in); + private static Size TELE_HOLE_SPACING = new Size(1.135d, SizeUnit.in); + private static Size POINT_SIZE = new Size(3d, SizeUnit.mm); private static Size HOLE_SIZE = new Size(2d, SizeUnit.mm); private static Size HOLE_MARGIN = new Size(4d, SizeUnit.mm); @@ -61,6 +72,7 @@ public class SingleCoilPickup extends AbstractTransparentComponent { private Orientation orientation = Orientation.DEFAULT; private Color color = BODY_COLOR; private Color baseColor = BASE_COLOR; + private SingleCoilType type = SingleCoilType.Stratocaster; @Override public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project, @@ -77,7 +89,10 @@ public class SingleCoilPickup extends AbstractTransparentComponent { g2d.setColor(getBaseColor()); g2d.fill(body[4]); g2d.setColor(getColor()); - g2d.fill(body[0]); + if (body[0] == null) + g2d.fill(body[3]); + else + g2d.fill(body[0]); g2d.setColor(POINT_COLOR); g2d.fill(body[1]); @@ -115,7 +130,8 @@ public class SingleCoilPickup extends AbstractTransparentComponent { } g2d.setColor(finalBorderColor); - g2d.draw(body[0]); + if (body[0] != null) + g2d.draw(body[0]); g2d.draw(body[3]); if (componentState != ComponentState.DRAGGING && !outlineMode) { @@ -139,7 +155,7 @@ public class SingleCoilPickup extends AbstractTransparentComponent { } g2d.setColor(finalLabelColor); g2d.setFont(LABEL_FONT); - Rectangle bounds = body[0].getBounds(); + Rectangle bounds = body[3].getBounds(); drawCenteredText(g2d, value, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, HorizontalAlignment.CENTER, VerticalAlignment.CENTER); } @@ -153,26 +169,69 @@ public class SingleCoilPickup extends AbstractTransparentComponent { int y = controlPoint.y; int width = (int) WIDTH.convertToPixels(); int length = (int) LENGTH.convertToPixels(); - int innerLength = (int) INNER_LENGTH.convertToPixels(); - int lipWidth = (int) LIP_WIDTH.convertToPixels(); - int lipLength = (int) LIP_LENGTH.convertToPixels(); + int stratInnerLength = (int) STRAT_INNER_LENGTH.convertToPixels(); + int teleLength = (int) TELE_LENGTH.convertToPixels(); + int teleBaseWidth = (int) TELE_BASE_WIDTH.convertToPixels(); + int teleLipLength = getClosestOdd(TELE_LIP_LENGTH.convertToPixels()); + int teleHoleSpacing = (int) TELE_HOLE_SPACING.convertToPixels(); + int coilLength = getType() == SingleCoilType.Stratocaster ? stratInnerLength : teleLength; + int lipWidth = (int) STRAT_LIP_WIDTH.convertToPixels(); + int lipLength = (int) STRAT_LIP_LENGTH.convertToPixels(); int pointSize = getClosestOdd(POINT_SIZE.convertToPixels()); int holeSize = getClosestOdd(HOLE_SIZE.convertToPixels()); int holeMargin = getClosestOdd(HOLE_MARGIN.convertToPixels()); + int baseRadius = (int) BASE_RADIUS.convertToPixels(); + int coilOffset = 0; - Area mainArea = - new Area(new RoundRectangle2D.Double(x - length / 2, y - lipWidth / 2 - width, length, width, width, width)); - // Cutout holes - mainArea.subtract(new Area(new Ellipse2D.Double(x - length / 2 + holeMargin - holeSize / 2, y - lipWidth / 2 - - width / 2 - holeSize / 2, holeSize, holeSize))); - mainArea.subtract(new Area(new Ellipse2D.Double(x + length / 2 - holeMargin - holeSize / 2, y - lipWidth / 2 - - width / 2 - holeSize / 2, holeSize, holeSize))); + if (getType() == SingleCoilType.Stratocaster) { + coilOffset = lipWidth / 2; - body[0] = mainArea; - body[4] = - new Area(new Polygon(new int[] {(int) (x - length / 2 + width * 0.47), (int) (x + length / 2 - width * 0.47), - x + lipLength / 2, x - lipLength / 2}, new int[] {y - lipWidth / 2, y - lipWidth / 2, y + lipWidth / 2, - y + lipWidth / 2}, 4)); + Area mainArea = + new Area(new RoundRectangle2D.Double(x - length / 2, y - lipWidth / 2 - width, length, width, width, width)); + // Cutout holes + mainArea.subtract(new Area(new Ellipse2D.Double(x - length / 2 + holeMargin - holeSize / 2, y - lipWidth / 2 + - width / 2 - holeSize / 2, holeSize, holeSize))); + mainArea.subtract(new Area(new Ellipse2D.Double(x + length / 2 - holeMargin - holeSize / 2, y - lipWidth / 2 + - width / 2 - holeSize / 2, holeSize, holeSize))); + + body[0] = mainArea; + RoundedPath basePath = new RoundedPath(baseRadius); + basePath.moveTo(x, y + lipWidth / 2); + basePath.lineTo(x + lipLength / 2, y + lipWidth / 2); + basePath.lineTo(x + length / 2, y - lipWidth); + basePath.lineTo(x - length / 2, y - lipWidth); + basePath.lineTo(x - lipLength / 2, y + lipWidth / 2); + basePath.lineTo(x, y + lipWidth / 2); + + Area base = new Area(basePath.getPath()); + base.subtract(mainArea); + + body[4] = base; + } else if (getType() == SingleCoilType.Telecaster) { + coilOffset = (teleBaseWidth - width) / 4; + + RoundedPath basePath = new RoundedPath(baseRadius); + basePath.moveTo(x, y + coilOffset); + basePath.lineTo(x + teleLipLength / 2, y + coilOffset); + basePath.lineTo(x + coilLength * 0.53, y - coilOffset - width / 2); + basePath.lineTo(x + coilLength / 2 - width * 0.45, y - coilOffset - width); + basePath.lineTo(x, y - 3 * coilOffset - width); + basePath.lineTo(x - coilLength / 2 + width * 0.45, y - coilOffset - width); + basePath.lineTo(x - coilLength * 0.53, y - coilOffset - width / 2); + basePath.lineTo(x - teleLipLength / 2, y + coilOffset); + basePath.lineTo(x, y + coilOffset); + + Area base = new Area(basePath.getPath()); + base.intersect(new Area(new Rectangle2D.Double(x - coilLength * 0.48, y - teleBaseWidth, coilLength * 0.96, + teleBaseWidth * 2))); + + // Cutout holes + base.subtract(new Area(new Ellipse2D.Double(x - teleLipLength / 2 - holeSize / 2, y, holeSize, holeSize))); + base.subtract(new Area(new Ellipse2D.Double(x + teleLipLength / 2 - holeSize / 2, y, holeSize, holeSize))); + base.subtract(new Area(new Ellipse2D.Double(x - holeSize / 2, y - teleHoleSpacing, holeSize, holeSize))); + + body[4] = base; + } body[1] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2, pointSize, pointSize)); @@ -182,14 +241,17 @@ public class SingleCoilPickup extends AbstractTransparentComponent { Area poleArea = new Area(); for (int i = 0; i < 6; i++) { Ellipse2D pole = - new Ellipse2D.Double(x - length / 2 + poleMargin + i * poleSpacing - poleSize / 2, y - lipWidth / 2 - width + new Ellipse2D.Double(x - length / 2 + poleMargin + i * poleSpacing - poleSize / 2, y - coilOffset - width / 2 - poleSize / 2, poleSize, poleSize); poleArea.add(new Area(pole)); } body[2] = poleArea; body[3] = - new RoundRectangle2D.Double(x - innerLength / 2, y - lipWidth / 2 - width, innerLength, width, width, width); + new Area(new RoundRectangle2D.Double(x - coilLength / 2, y - coilOffset - width, coilLength, width, width, + width)); + + ((Area) body[4]).subtract((Area) body[3]); // Rotate if needed if (orientation != Orientation.DEFAULT) { @@ -208,7 +270,8 @@ public class SingleCoilPickup extends AbstractTransparentComponent { AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y); for (Shape shape : body) { Area area = (Area) shape; - area.transform(rotation); + if (area != null) + area.transform(rotation); } } } @@ -244,6 +307,19 @@ public class SingleCoilPickup extends AbstractTransparentComponent { } } + @EditableProperty + public SingleCoilType getType() { + if (type == null) + type = SingleCoilType.Stratocaster; + return type; + } + + public void setType(SingleCoilType type) { + this.type = type; + // Invalidate the body + body = null; + } + @Override public int getControlPointCount() { return 1; @@ -312,4 +388,8 @@ public class SingleCoilPickup extends AbstractTransparentComponent { public void setBaseColor(Color baseColor) { this.baseColor = baseColor; } + + public enum SingleCoilType { + Stratocaster, Telecaster; + } } diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index dcfe4735..427ce079 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index 66b408e8..a26b3413 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ diff --git a/diylc/diylc-swing/update.xml b/diylc/diylc-swing/update.xml index 0d9a25d4..b7edebe6 100644 --- a/diylc/diylc-swing/update.xml +++ b/diylc/diylc-swing/update.xml @@ -1466,6 +1466,10 @@ NEW_FEATURE P-90 pickup in both "dog ear" and "soap bar" variations added + + NEW_FEATURE + Telecaster bridge pickup added as a variation of single coil pickup + NEW_FEATURE Mini humbucker pickup added as a variation of Humbucker pickup