/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package hr.fer.zemris.vhdllab.applets.editor.schema2.model;
import hr.fer.zemris.vhdllab.applets.editor.schema2.exceptions.NotImplementedException;
import hr.fer.zemris.vhdllab.applets.editor.schema2.exceptions.ParameterNotFoundException;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameter;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameterCollection;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaWire;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IWireDrawer;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.Caseless;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.Rect2d;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.SMath;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.WireSegment;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.XYLocation;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.drawers.DefaultWireDrawer;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.parameters.CaselessParameter;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.parameters.ParameterFactory;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.parameters.events.NameChanger;
import hr.fer.zemris.vhdllab.applets.editor.schema2.predefined.beans.ParameterWrapper;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SchemaWire implements ISchemaWire {
private List<XYLocation> nodes;
private Map<XYLocation, Integer> nodeuses;
private Set<XYLocation> edgepoints;
private IParameterCollection parameters;
private List<WireSegment> segments;
private IWireDrawer wiredrawer;
/**
* Za deserijalizaciju.
*/
public SchemaWire() {
create();
}
/**
* Koristiti ovaj konstruktor.
*/
public SchemaWire(Caseless wireName) {
create();
initDefaultParameters(wireName);
}
private void initDefaultParameters(Caseless wireName) {
CaselessParameter caspar = new CaselessParameter(ISchemaWire.KEY_NAME, false, wireName);
caspar.setParameterEvent(new NameChanger());
parameters.addParameter(caspar);
}
private void create() {
nodes = new ArrayList<XYLocation>();
nodeuses = new HashMap<XYLocation, Integer>();
edgepoints = new HashSet<XYLocation>();
parameters = new SchemaParameterCollection();
segments = new ArrayList<WireSegment>();
wiredrawer = new DefaultWireDrawer(this);
}
public ISchemaWire copyCtor() {
throw new NotImplementedException();
}
public Rect2d getBounds() {
Rect2d r = new Rect2d();
int xmax = 0, ymax = 0;
if (segments.size() > 0) {
r.left = SMath.min(segments.get(0).getStart().x, segments.get(0).getEnd().x);
xmax = SMath.max(segments.get(0).getStart().x, segments.get(0).getEnd().x);
r.top = SMath.min(segments.get(0).getStart().y, segments.get(0).getEnd().y);
ymax = SMath.max(segments.get(0).getStart().y, segments.get(0).getEnd().y);
}
for (int i = 1; i < segments.size(); i++) {
r.left = SMath.min(segments.get(i).getStart().x, r.left);
r.left = SMath.min(segments.get(i).getEnd().x, r.left);
xmax = SMath.max(segments.get(i).getStart().x, xmax);
xmax = SMath.max(segments.get(i).getEnd().x, xmax);
r.top = SMath.min(segments.get(i).getStart().y, r.top);
r.top = SMath.min(segments.get(i).getEnd().y, r.top);
ymax = SMath.max(segments.get(i).getStart().y, ymax);
ymax = SMath.max(segments.get(i).getEnd().y, ymax);
}
r.width = xmax - r.left;
r.height = ymax - r.top;
return r;
}
public IWireDrawer getDrawer() {
return wiredrawer;
}
public Caseless getName() {
try {
return (Caseless)(parameters.getValue(ISchemaWire.KEY_NAME));
} catch (ParameterNotFoundException e) {
throw new RuntimeException("Name parameter not found.");
}
}
public List<XYLocation> getNodes() {
return nodes;
}
public IParameterCollection getParameters() {
return parameters;
}
public List<WireSegment> getSegments() {
return segments;
}
/**
* Provjerava da li se segment preklapa s nekim
* od postojecih segmenata.
* @param segment
* Segment NIJE u listi segmenata.
* @return
* Vraca true ako postoji preklapanje.
*/
private boolean doesOverlap(WireSegment segment) {
XYLocation start = segment.getStart(), end = segment.getEnd();
if (segment.isVertical()) {
/* segment vertical */
for (WireSegment ws : segments) {
if (!ws.isVertical()) continue;
XYLocation wsst = ws.getStart(), wsnd = ws.getEnd();
if ((wsst.x == start.x) &&
(SMath.insideOrd(start.y, wsst.y, wsnd.y) || SMath.insideOrd(end.y, wsst.y, wsnd.y)))
return true;
}
} else {
/* segment horizontal */
for (WireSegment ws : segments) {
if (ws.isVertical()) continue;
XYLocation wsst = ws.getStart(), wsnd = ws.getEnd();
if ((wsst.y == start.y) &&
(SMath.insideOrd(start.x, wsst.x, wsnd.x) || SMath.insideOrd(end.x, wsst.x, wsnd.x)))
return true;
}
}
return false;
}
public Set<WireSegment> segmentsAt(int x, int y) {
Set<WireSegment> wsset = null;
for (WireSegment seg : segments) {
if (seg.hasPoint(x, y)) {
if (wsset == null) wsset = new HashSet<WireSegment>();
wsset.add(seg);
}
}
return wsset;
}
public boolean insertSegment(WireSegment segment) {
/* check overlap */
if (doesOverlap(segment)) return false;
/* find intersections and edgepoints for this segment */
List<XYLocation> segnodes = findIntersections(segment);
List<XYLocation> segedges = findEdgepoints(segment);
/* add segment and nodes for intersections */
segments.add(segment);
for (XYLocation node : segnodes) {
addAndMapNode(node);
}
/* handle edge points */
for (XYLocation ep : segedges) {
processEdgepointAddition(ep);
}
return true;
}
private void processEdgepointAddition(XYLocation ep) {
if (edgepoints.contains(ep)) {
/* edge point already exists - check for a node */
if (nodeuses.containsKey(ep)) {
/* node already exists too - increase counter */
nodeuses.put(ep, nodeuses.get(ep) + 1);
} else {
/* node does not exist - add it */
nodes.add(ep);
nodeuses.put(ep, 1);
}
} else {
/* edge point does not exist - add it */
edgepoints.add(ep);
}
}
/**
* Vraca popis dodira rubova vezanih uz segment.
* BITNO: Pritom se pretpostavlja da segment
* jos nije u listi segmenata.
* @param segment
* @return
*/
private List<XYLocation> findEdgepoints(WireSegment segment) {
Set<XYLocation> edges = new HashSet<XYLocation>();
/* find all edge points */
for (WireSegment ws : segments) {
XYLocation edgepoint = ws.edgepoint(segment);
if (edgepoint != null) edges.add(edgepoint);
}
return new ArrayList<XYLocation>(edges);
}
/**
* Vraca popis presjecista vezanih uz segment.
* BITNO: Pretpostavlja da zadani segment NIJE
* u listi segmenata.
* @param segment
* Segment koji se zeli dodati, ili koji je upravo
* obrisan, a koji NIJE u listi segmenata.
* @return
* Praznu listu ako nema presjecista. Nece vratiti null.
*/
private List<XYLocation> findIntersections(WireSegment segment) {
List<XYLocation> nodes = new ArrayList<XYLocation>();
/* find all intersections */
for (WireSegment ws : segments) {
XYLocation intersectpoint = ws.intersection(segment);
if (intersectpoint != null) nodes.add(intersectpoint);
}
return nodes;
}
public boolean removeSegment(WireSegment segment) {
if (!segments.contains(segment)) return false;
/* first remove segment */
segments.remove(segment);
/* search for intersections and edgepoints caused by this segment */
List<XYLocation> segnodes = findIntersections(segment);
List<XYLocation> segedges = findEdgepoints(segment);
for (XYLocation node : segnodes) {
removeAndDemapNode(node);
}
/* handle edge points */
for (XYLocation ep : segedges) {
processEdgepointRemoval(ep);
}
return true;
}
private void processEdgepointRemoval(XYLocation ep) {
if (nodeuses.containsKey(ep)) {
/* node exists at location */
int uses = nodeuses.get(ep);
if (uses > 1) {
/* other segments share this node - decrease counter */
nodeuses.put(ep, uses - 1);
} else {
/* this node is obsolete - delete it */
nodeuses.remove(ep);
nodes.remove(ep);
}
} else {
/* no node exists at location - remove edge point */
edgepoints.remove(ep);
}
}
/**
* Dodaje cvor ako on vec ne postoji, te dodatno
* povecava brojac segmenata koji dijele taj cvor.
* @param node
*/
private void addAndMapNode(XYLocation node) {
if (!nodeuses.containsKey(node)) {
/* this node does not exist yet */
nodes.add(node);
nodeuses.put(node, 1);
} else {
/* node already exists */
nodeuses.put(node, nodeuses.get(node) + 1);
}
}
/**
* Mice cvor ako on postoji, te dodatno smanjuje brojac segmenata
* koji koriste taj cvor.
* Ako cvor ne postoji, ne cini nista.
* @param node
*/
private void removeAndDemapNode(XYLocation node) {
if (nodeuses.containsKey(node)) { /* node exists */
int uses = nodeuses.get(node);
if (uses > 1) { /* other segments share this node */
nodeuses.put(node, uses - 1);
} else { /* no other segment uses this node */
nodeuses.remove(node);
nodes.remove(node);
}
}
}
public void setName(String name) {
try {
parameters.setValue(ISchemaWire.KEY_NAME, new Caseless(name));
} catch (ParameterNotFoundException e) {
throw new RuntimeException("Parameter name could not be set.");
}
}
public void setDrawer(String drawerName) {
try {
Class cls = Class.forName(drawerName);
Class[] partypes = new Class[1];
partypes[0] = ISchemaWire.class;
Constructor<IWireDrawer> ct = cls.getConstructor(partypes);
wiredrawer = ct.newInstance();
} catch (Exception e) {
wiredrawer = new DefaultWireDrawer(this);
}
}
/**
* Dodaje segment zice u listu segmenata,
* ne dodajuci pritom cvorove na presjecista.
*
* @param segment
*/
public void addWireSegment(WireSegment segment) {
insertSegment(segment);
}
public void addParameter(ParameterWrapper pw) {
IParameter param = ParameterFactory.createParameter(pw);
parameters.addParameter(param);
}
/**
* Trazi cvorove koji su povezani s ovim segmentom.
*
* @param segadded
* Ako je segment vec u listi segmenata, ovaj parametar
* mora biti true. U tom slucaju ce se vratiti samo oni
* cvorovi koji postoje radi ovog segmenta, i ujedno su
* u listi cvorova. U protivnom se nadeni cvorovi nece
* usporedivati s listom postojecih cvorova.
*
* @param exclusive
* Ako je naveden true, funkcija ce vratiti samo one cvorove
* koji su vezani iskljucivo uz navedeni segment.
*/
@Deprecated
@SuppressWarnings("unused")
private List<XYLocation> findNodesForSegment(WireSegment segment, boolean segadded, boolean exclusive) {
List<XYLocation> segnodes = new ArrayList<XYLocation>();
for (WireSegment otherseg : segments) {
if (segadded && segment.equals(otherseg)) continue;
// find a crosspoint, see if it is exclusive
XYLocation crosspoint = segment.intersection(otherseg);
if (crosspoint != null) {
if (segadded && !nodes.contains(crosspoint)) continue;
if (exclusive) {
// see if other segments cause this intersection
boolean found = false;
for (WireSegment anyother : segments) {
if (anyother.equals(segment) || anyother.equals(otherseg)) continue;
if (anyother.intersection(otherseg) != null) {
found = true;
break;
}
}
if (found) continue;
}
segnodes.add(crosspoint);
continue;
}
// find an edgepoint
crosspoint = segment.edgepoint(otherseg);
if (crosspoint != null) {
if (segadded && !nodes.contains(crosspoint)) continue;
// now see whether this edgepoint is shared, and by how many segments
int count = 0;
for (WireSegment anyother : segments) {
if (anyother.equals(segment) || anyother.equals(otherseg)) continue;
if (anyother.edgepoint(otherseg) != null) count++;
}
if (count == 0) continue;
if (exclusive && count > 1) continue;
segnodes.add(crosspoint);
}
}
return segnodes;
}
}