mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 10:42:09 +10:00
consolidate pin color and pin rendering
This commit is contained in:
@@ -1,357 +1,359 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 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.components;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link IDIYComponent} that contains component name and toString.
|
||||
*
|
||||
* IMPORTANT: to improve performance, all fields except for <code>Point</code> and
|
||||
* <code>Point</code> arrays should be immutable. Failing to comply with this can result in annoying
|
||||
* and hard to trace bugs.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractComponent<T> implements IDIYComponent<T> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected UUID id = UUID.randomUUID();
|
||||
protected String name = "";
|
||||
|
||||
public static Color SELECTION_COLOR = Color.red;
|
||||
public static Color LABEL_COLOR = Color.black;
|
||||
public static Color LABEL_COLOR_SELECTED = Color.red;
|
||||
public static Font LABEL_FONT = new Font("Square721 BT", Font.PLAIN, 14);
|
||||
public static Color METAL_COLOR = Color.decode("#759DAF");
|
||||
public static Color LIGHT_METAL_COLOR = Color.decode("#EEEEEE");
|
||||
public static Color COPPER_COLOR = Color.decode("#DA8A67");
|
||||
public static Color GOLD_COLOR = Color.decode("#F4B301");
|
||||
public static Color BRASS_COLOR = Color.decode("#B8860B");
|
||||
public static Color FR4_COLOR = Color.decode("#6B9B6B");
|
||||
public static Color FR4_LIGHT_COLOR = Color.decode("#BABE71");
|
||||
public static Color PHENOLIC_COLOR = Color.decode("#F8EBB3");
|
||||
public static Color PHENOLIC_DARK_COLOR = Color.decode("#CD8500");
|
||||
|
||||
@EditableProperty(defaultable = false)
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
if (id == null) {
|
||||
id = UUID.randomUUID();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canControlPointOverlap(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name == null ? "(null)" : name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueForDisplay() {
|
||||
if (getValue() == null)
|
||||
return "";
|
||||
|
||||
Object val = getValue();
|
||||
if (val.getClass().isArray()) {
|
||||
return String.join(" / ", Arrays.stream((Object[])val).map(x -> x == null ? "" : x.toString()).collect(Collectors.toList()));
|
||||
}
|
||||
return val.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest odd number, i.e. x when x is odd, or x + 1 when x is even.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
protected int getClosestOdd(double x) {
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest odd number, i.e. x when x is odd, or x + 1 when x is even.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
protected int getClosestOdd(Size s) {
|
||||
double x = s.convertToPixels();
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns darker color if possible, or lighter if it's already dark
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
protected Color darkerOrLighter(Color color) {
|
||||
float[] hsb = new float[3];
|
||||
Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb);
|
||||
return new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2] > 0.5 ? hsb[2] - 0.25f : hsb[2] + 0.25f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clip
|
||||
* @return true if none of the control points lie in the clip rectangle.
|
||||
*/
|
||||
protected boolean checkPointsClipped(Shape clip) {
|
||||
if (clip == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
if (clip.contains(getControlPoint(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
double minX = Integer.MAX_VALUE;
|
||||
double minY = Integer.MAX_VALUE;
|
||||
double maxX = Integer.MIN_VALUE;
|
||||
double maxY = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
if (minX > p.getX()) {
|
||||
minX = p.getX();
|
||||
}
|
||||
if (maxX < p.getX()) {
|
||||
maxX = p.getX();
|
||||
}
|
||||
if (minY > p.getY()) {
|
||||
minY = p.getY();
|
||||
}
|
||||
if (maxY < p.getY()) {
|
||||
maxY = p.getY();
|
||||
}
|
||||
}
|
||||
double margin = getClipMargin();
|
||||
Rectangle2D rect = new Rectangle2D.Double(minX - margin, minY - margin,
|
||||
maxX - minX + 2 * margin, maxY - minY + 2 * margin);
|
||||
return !clip.intersects(rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance in pixels that the drawing may reach beyond the bounding box of the control points,
|
||||
* such as half the width of a stroke running through them. {@link #checkPointsClipped(Shape)}
|
||||
* widens the box by this much before testing it against the clip. The margin must stay positive,
|
||||
* because {@link Shape#intersects(Rectangle2D)} reports a box with no width or height as disjoint
|
||||
* from everything, which would hide a straight horizontal or vertical component whose control
|
||||
* points all lie outside the clip.
|
||||
*
|
||||
* @return margin around the control points.
|
||||
*/
|
||||
protected double getClipMargin() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public IDIYComponent<T> clone() throws CloneNotSupportedException {
|
||||
try {
|
||||
// Instantiate object of the same type
|
||||
Class<?> clazz = this.getClass();
|
||||
AbstractComponent<T> newInstance = (AbstractComponent<T>) clazz.getDeclaredConstructor().newInstance();
|
||||
while (AbstractComponent.class.isAssignableFrom(clazz)) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
clazz = clazz.getSuperclass();
|
||||
// fields = this.getClass().getDeclaredFields();
|
||||
// Copy over all non-static, non-final fields that are declared
|
||||
// in AbstractComponent or one of it's child classes
|
||||
for (Field field : fields) {
|
||||
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
Object value = field.get(this);
|
||||
|
||||
if (field.getName().equalsIgnoreCase("id")) {
|
||||
value = getId();
|
||||
}
|
||||
|
||||
// Deep copy point arrays.
|
||||
// TODO: something nicer
|
||||
if (value != null && value.getClass().isArray()
|
||||
&& Point2D.class.isAssignableFrom(value.getClass().getComponentType())) {
|
||||
Object newArray = Array.newInstance(value.getClass().getComponentType(), Array.getLength(value));
|
||||
for (int i = 0; i < Array.getLength(value); i++) {
|
||||
Point2D p = (Point2D) Array.get(value, i);
|
||||
if (p != null)
|
||||
Array.set(newArray, i, new Point2D.Double(p.getX(), p.getY()));
|
||||
}
|
||||
value = newArray;
|
||||
}
|
||||
// Deep copy points.
|
||||
// TODO: something nicer
|
||||
if (value != null && value instanceof Point2D) {
|
||||
Point2D p = (Point2D) value;
|
||||
value = new Point2D.Double(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
field.set(newInstance, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newInstance;
|
||||
} catch (Exception e) {
|
||||
throw new CloneNotSupportedException("Could not clone the component. Reason: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsTo(IDIYComponent<?> other) {
|
||||
if (other == null)
|
||||
return false;
|
||||
if (!other.getClass().equals(this.getClass()))
|
||||
return false;
|
||||
Class<?> clazz = this.getClass();
|
||||
while (AbstractComponent.class.isAssignableFrom(clazz)) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
clazz = clazz.getSuperclass();
|
||||
// fields = this.getClass().getDeclaredFields();
|
||||
// Copy over all non-static, non-final and non-transient fields that are declared
|
||||
// in
|
||||
// AbstractComponent or one of it's child classes
|
||||
for (Field field : fields) {
|
||||
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) &&
|
||||
!Modifier.isTransient(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object value = field.get(this);
|
||||
Object otherValue = field.get(other);
|
||||
if (!compareObjects(value, otherValue))
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean compareObjects(Object o1, Object o2) {
|
||||
if (o1 == null && o2 == null)
|
||||
return true;
|
||||
if (o1 == null || o2 == null)
|
||||
return false;
|
||||
if (o1.getClass().isArray()) {
|
||||
if (o1.getClass().getComponentType() == byte.class)
|
||||
return Arrays.equals((byte[]) o1, (byte[]) o2);
|
||||
if (o1.getClass().getComponentType() == double.class)
|
||||
return Arrays.equals((double[]) o1, (double[]) o2);
|
||||
if (o1.getClass().getComponentType() == int.class)
|
||||
return Arrays.equals((int[]) o1, (int[]) o2);
|
||||
return Arrays.equals((Object[]) o1, (Object[]) o2);
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInternalLinkName(int index1, int index2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stretchBetween(Point2D first, Point2D second) {
|
||||
int count = getControlPointCount();
|
||||
// fresh instances because implementations are free to keep the point objects they are handed
|
||||
setControlPoint(new Point2D.Double(first.getX(), first.getY()), 0);
|
||||
if (count > 1) {
|
||||
setControlPoint(new Point2D.Double(second.getX(), second.getY()), count - 1);
|
||||
}
|
||||
for (int i = 1; i < count - 1; i++) {
|
||||
setControlPoint(getStretchedPoint(first, second, i), i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides where a control point that is neither of the two ends lands when the component is
|
||||
* stretched between them, see {@link #stretchBetween(Point2D, Point2D)}. The default spaces the
|
||||
* points evenly along the straight line joining the ends. Override to place a point that carries
|
||||
* a meaning of its own, such as one that positions a label.
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
* @param index
|
||||
* @return position of the control point at the specified index.
|
||||
*/
|
||||
protected Point2D getStretchedPoint(Point2D first, Point2D second, int index) {
|
||||
double t = (double) index / (getControlPointCount() - 1);
|
||||
return new Point2D.Double(first.getX() + t * (second.getX() - first.getX()),
|
||||
first.getY() + t * (second.getY() - first.getY()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createdIn(Project project) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 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.components;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link IDIYComponent} that contains component name and toString.
|
||||
*
|
||||
* IMPORTANT: to improve performance, all fields except for <code>Point</code> and
|
||||
* <code>Point</code> arrays should be immutable. Failing to comply with this can result in annoying
|
||||
* and hard to trace bugs.
|
||||
*
|
||||
* @author Branislav Stojkovic
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractComponent<T> implements IDIYComponent<T> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected UUID id = UUID.randomUUID();
|
||||
protected String name = "";
|
||||
|
||||
public static Color SELECTION_COLOR = Color.red;
|
||||
public static Color LABEL_COLOR = Color.black;
|
||||
public static Color LABEL_COLOR_SELECTED = Color.red;
|
||||
public static Font LABEL_FONT = new Font("Square721 BT", Font.PLAIN, 14);
|
||||
public static Color METAL_COLOR = Color.decode("#759DAF");
|
||||
public static Color LIGHT_METAL_COLOR = Color.decode("#EEEEEE");
|
||||
public static Color PIN_COLOR = Color.decode("#00B2EE");
|
||||
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
|
||||
public static Color COPPER_COLOR = Color.decode("#DA8A67");
|
||||
public static Color GOLD_COLOR = Color.decode("#F4B301");
|
||||
public static Color BRASS_COLOR = Color.decode("#B8860B");
|
||||
public static Color FR4_COLOR = Color.decode("#6B9B6B");
|
||||
public static Color FR4_LIGHT_COLOR = Color.decode("#BABE71");
|
||||
public static Color PHENOLIC_COLOR = Color.decode("#F8EBB3");
|
||||
public static Color PHENOLIC_DARK_COLOR = Color.decode("#CD8500");
|
||||
|
||||
@EditableProperty(defaultable = false)
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
if (id == null) {
|
||||
id = UUID.randomUUID();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canControlPointOverlap(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name == null ? "(null)" : name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueForDisplay() {
|
||||
if (getValue() == null)
|
||||
return "";
|
||||
|
||||
Object val = getValue();
|
||||
if (val.getClass().isArray()) {
|
||||
return String.join(" / ", Arrays.stream((Object[])val).map(x -> x == null ? "" : x.toString()).collect(Collectors.toList()));
|
||||
}
|
||||
return val.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest odd number, i.e. x when x is odd, or x + 1 when x is even.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
protected int getClosestOdd(double x) {
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest odd number, i.e. x when x is odd, or x + 1 when x is even.
|
||||
*
|
||||
* @param x
|
||||
* @return
|
||||
*/
|
||||
protected int getClosestOdd(Size s) {
|
||||
double x = s.convertToPixels();
|
||||
return ((int) x / 2) * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns darker color if possible, or lighter if it's already dark
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
protected Color darkerOrLighter(Color color) {
|
||||
float[] hsb = new float[3];
|
||||
Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb);
|
||||
return new Color(Color.HSBtoRGB(hsb[0], hsb[1], hsb[2] > 0.5 ? hsb[2] - 0.25f : hsb[2] + 0.25f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clip
|
||||
* @return true if none of the control points lie in the clip rectangle.
|
||||
*/
|
||||
protected boolean checkPointsClipped(Shape clip) {
|
||||
if (clip == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
if (clip.contains(getControlPoint(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
double minX = Integer.MAX_VALUE;
|
||||
double minY = Integer.MAX_VALUE;
|
||||
double maxX = Integer.MIN_VALUE;
|
||||
double maxY = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < getControlPointCount(); i++) {
|
||||
Point2D p = getControlPoint(i);
|
||||
if (minX > p.getX()) {
|
||||
minX = p.getX();
|
||||
}
|
||||
if (maxX < p.getX()) {
|
||||
maxX = p.getX();
|
||||
}
|
||||
if (minY > p.getY()) {
|
||||
minY = p.getY();
|
||||
}
|
||||
if (maxY < p.getY()) {
|
||||
maxY = p.getY();
|
||||
}
|
||||
}
|
||||
double margin = getClipMargin();
|
||||
Rectangle2D rect = new Rectangle2D.Double(minX - margin, minY - margin,
|
||||
maxX - minX + 2 * margin, maxY - minY + 2 * margin);
|
||||
return !clip.intersects(rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance in pixels that the drawing may reach beyond the bounding box of the control points,
|
||||
* such as half the width of a stroke running through them. {@link #checkPointsClipped(Shape)}
|
||||
* widens the box by this much before testing it against the clip. The margin must stay positive,
|
||||
* because {@link Shape#intersects(Rectangle2D)} reports a box with no width or height as disjoint
|
||||
* from everything, which would hide a straight horizontal or vertical component whose control
|
||||
* points all lie outside the clip.
|
||||
*
|
||||
* @return margin around the control points.
|
||||
*/
|
||||
protected double getClipMargin() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public IDIYComponent<T> clone() throws CloneNotSupportedException {
|
||||
try {
|
||||
// Instantiate object of the same type
|
||||
Class<?> clazz = this.getClass();
|
||||
AbstractComponent<T> newInstance = (AbstractComponent<T>) clazz.getDeclaredConstructor().newInstance();
|
||||
while (AbstractComponent.class.isAssignableFrom(clazz)) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
clazz = clazz.getSuperclass();
|
||||
// fields = this.getClass().getDeclaredFields();
|
||||
// Copy over all non-static, non-final fields that are declared
|
||||
// in AbstractComponent or one of it's child classes
|
||||
for (Field field : fields) {
|
||||
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
Object value = field.get(this);
|
||||
|
||||
if (field.getName().equalsIgnoreCase("id")) {
|
||||
value = getId();
|
||||
}
|
||||
|
||||
// Deep copy point arrays.
|
||||
// TODO: something nicer
|
||||
if (value != null && value.getClass().isArray()
|
||||
&& Point2D.class.isAssignableFrom(value.getClass().getComponentType())) {
|
||||
Object newArray = Array.newInstance(value.getClass().getComponentType(), Array.getLength(value));
|
||||
for (int i = 0; i < Array.getLength(value); i++) {
|
||||
Point2D p = (Point2D) Array.get(value, i);
|
||||
if (p != null)
|
||||
Array.set(newArray, i, new Point2D.Double(p.getX(), p.getY()));
|
||||
}
|
||||
value = newArray;
|
||||
}
|
||||
// Deep copy points.
|
||||
// TODO: something nicer
|
||||
if (value != null && value instanceof Point2D) {
|
||||
Point2D p = (Point2D) value;
|
||||
value = new Point2D.Double(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
field.set(newInstance, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newInstance;
|
||||
} catch (Exception e) {
|
||||
throw new CloneNotSupportedException("Could not clone the component. Reason: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsTo(IDIYComponent<?> other) {
|
||||
if (other == null)
|
||||
return false;
|
||||
if (!other.getClass().equals(this.getClass()))
|
||||
return false;
|
||||
Class<?> clazz = this.getClass();
|
||||
while (AbstractComponent.class.isAssignableFrom(clazz)) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
clazz = clazz.getSuperclass();
|
||||
// fields = this.getClass().getDeclaredFields();
|
||||
// Copy over all non-static, non-final and non-transient fields that are declared
|
||||
// in
|
||||
// AbstractComponent or one of it's child classes
|
||||
for (Field field : fields) {
|
||||
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) &&
|
||||
!Modifier.isTransient(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object value = field.get(this);
|
||||
Object otherValue = field.get(other);
|
||||
if (!compareObjects(value, otherValue))
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean compareObjects(Object o1, Object o2) {
|
||||
if (o1 == null && o2 == null)
|
||||
return true;
|
||||
if (o1 == null || o2 == null)
|
||||
return false;
|
||||
if (o1.getClass().isArray()) {
|
||||
if (o1.getClass().getComponentType() == byte.class)
|
||||
return Arrays.equals((byte[]) o1, (byte[]) o2);
|
||||
if (o1.getClass().getComponentType() == double.class)
|
||||
return Arrays.equals((double[]) o1, (double[]) o2);
|
||||
if (o1.getClass().getComponentType() == int.class)
|
||||
return Arrays.equals((int[]) o1, (int[]) o2);
|
||||
return Arrays.equals((Object[]) o1, (Object[]) o2);
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return Integer.toString(index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInternalLinkName(int index1, int index2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPointMoveFreely(int pointIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stretchBetween(Point2D first, Point2D second) {
|
||||
int count = getControlPointCount();
|
||||
// fresh instances because implementations are free to keep the point objects they are handed
|
||||
setControlPoint(new Point2D.Double(first.getX(), first.getY()), 0);
|
||||
if (count > 1) {
|
||||
setControlPoint(new Point2D.Double(second.getX(), second.getY()), count - 1);
|
||||
}
|
||||
for (int i = 1; i < count - 1; i++) {
|
||||
setControlPoint(getStretchedPoint(first, second, i), i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides where a control point that is neither of the two ends lands when the component is
|
||||
* stretched between them, see {@link #stretchBetween(Point2D, Point2D)}. The default spaces the
|
||||
* points evenly along the straight line joining the ends. Override to place a point that carries
|
||||
* a meaning of its own, such as one that positions a label.
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
* @param index
|
||||
* @return position of the control point at the specified index.
|
||||
*/
|
||||
protected Point2D getStretchedPoint(Point2D first, Point2D second, int index) {
|
||||
double t = (double) index / (getControlPointCount() - 1);
|
||||
return new Point2D.Double(first.getX() + t * (second.getX() - first.getX()),
|
||||
first.getY() + t * (second.getY() - first.getY()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createdIn(Project project) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Transistor pins are now drawn on top of the body in the same color on every package, so they stay visible whatever the body transparency</description>
|
||||
<description>Pins of transistors, 'SIP IC', 'DIP Switch' and 'Bridge Rectifier' are now drawn on top of the body, so they stay visible whatever the body transparency, and pins are drawn in the same color on every component, including 'DIP IC', 'Bridge Rectifier', 'LED Bar Graph', trimmers and PCB-mounted 'Tube Socket'</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
|
||||
Reference in New Issue
Block a user