/******************************************************************************* * 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.simulations; import java.awt.Dimension; import java.awt.Graphics; import java.util.Arrays; import javax.swing.JPanel; import javax.swing.JScrollBar; /** * Skala uzima vrijednosti koje generira VcdParser i vrsi sve proracune kod * event-hendlanja. Prilikom svakog povecanja proracunava trajanje valnih * oblika u pikselima. * * @author Boris Ozegovic */ class Scale extends JPanel { /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * femto sekundama */ private static final byte FEMTO_SECONDS = 1; /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * pico sekundama */ private static final double PICO_SECONDS = 1e-3; /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * nano sekundama */ private static final double NANO_SECONDS = 1e-6; /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * micro sekundama */ private static final double MICRO_SECONDS = 1e-9; /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * mili sekundama */ private static final double MILI_SECONDS = 1e-12; /** * Konstanta s kojom se mnozi trenutne vrijednosti ako je mjerna jedinica u * sekundama */ private static final double SECONDS = 1e-15; /** Razmak izmedu dvije tocke na skali je uvijek 100 piksela */ private static final int SCALE_STEP_IN_PIXELS = 100; /** Visina skale */ private static final int SCALE_HEIGHT = 30; /** Piksel na kojem pocinje iscrtavanje skale */ private static int SCALE_START_POINT_IN_PIXELS = 0; /** Svaki vrijednost na skali pocinje od 19. piksela */ private static final int SCALE_VALUE_YAXIS = 19; /** Os pocinje od 4. piksela */ private static final int SCALE_MAIN_LINE_YAXIS = 4; /** Svaka crtica pocinje od 2. piksela */ private static final int SCALE_TAG_LINE_YSTART = 2; /** Svaka crtica zavrsava na 6. pikselu */ private static final int SCALE_TAG_LINE_YEND = 6; /** Tocke u kojima nastaju promjene vrijednosti signala */ private long[] transitionPoints; /** GHDL simulator generira sve u femtosekundama */ private double[] durationsInFemtoSeconds; /** Trajanje pojedenih intervala u vremenskoj jedinici */ private int[] durationsInTime; /** Trajanje u pikselima koje id direktno za crtanje valnih oblika */ private int[] durationsInPixels; /** Minimalna vrijednost trajanja signala izemdu dvije promjene */ private int minimumDurationInTime; /** Povecava/smanjuje skalu za neku vrijednost */ private double scaleFactor = 1f; /** Povecava/smanjuje time/pixel faktor */ private double pixelFactor; /** Ime mjerne jedinice */ private String measureUnitName; /** Mjerna jedinica */ private double measureUnit; /** U pikselima - odreduje tocno odredeni piksel na kojem skala zavrsava */ private int scaleEndPointInPixels; /** Korak skale u vremenu, ovisno o scaleFactor */ private double scaleStepInTime; /** Vrijednost koja se kumulativno povecava, ovisno o scaleStepInTime */ private double scaleValue; /** * Ovisno o trenutnom offsetu scrollbara. Ako je offset npr. 1350, onda se * interno preracuna u 1300 i od 1300 se crta skala */ private int screenStartPointInPixels; /** * Podrazumijevana velicina ekrana - skale ne iscrtava automatski sve * vrijednost vec vrsi proracuna na temelju offseta i iscrtava samo trenutnu * velicinu komponente */ private int screenSizeInPixels; /** ScreenStartPointInPixels + trenutna duljina komponente */ private int screenEndPointInPixels; /** Sve podrzane mjerne jedinice **/ private final String[] units = {"fs", "ps", "ns", "us", "ms", "s", "ks", "ms"}; /** Offset trenutni */ private int offsetXAxis; /** Horizontalni scrollbar */ private JScrollBar horizontalScrollbar; /** Boje */ private ThemeColor themeColor; /** SerialVersionUID */ private static final long serialVersionUID = -4934785363261778378L; /** * Constructor * * @param horizontalScrollbar horizontalni scrollbar * @param themeColor trneutna tema */ public Scale (JScrollBar horizontalScrollbar, ThemeColor themeColor) { this.themeColor = themeColor; this.horizontalScrollbar = horizontalScrollbar; } /** * Metoda postavlja novu vrijednosti skale, u ovisnosti o rezultatu kojeg je * parsirao GHDLResults * * @param results rezultati koje je parsirao GhldResults */ public void setContent(GhdlResults results) { this.transitionPoints = results.getTransitionPoints(); durationsInFemtoSeconds = new double[transitionPoints.length - 1]; durationsInTime = new int[durationsInFemtoSeconds.length]; durationsInPixels = new int[durationsInFemtoSeconds.length]; for (int i = 0; i < durationsInFemtoSeconds.length; i++) { double operand1 = transitionPoints[i + 1]; double operand2 = transitionPoints[i]; durationsInFemtoSeconds[i] = operand1 - operand2; } // fix for http://morgoth.zemris.fer.hr/trac/vhdllab/ticket/39 if (Arrays.equals(durationsInFemtoSeconds, new double[] {0.0})) { durationsInFemtoSeconds[0] = 1.0; } /* crta pocetne valne oblike sa scaleFaktorom 1 */ drawDefaultWave(); } /** * Metoda koja crta pocetne oblike */ public void drawDefaultWave () { /* radi lokalnu kopiju jer sortiranje mijenja poredak */ double[] temporaryDurations = durationsInFemtoSeconds.clone(); Arrays.sort(temporaryDurations); /* * nakon sortiranja minimalno se trajanje nalazi na prvom mjestu u polju * i na temelju tog minimalnog trajanja odreduje pocetnu jedinicu (ns, * ps, itd). Castanje zbog toga da ne produ znamenke iza dec. zareza. * Pomocu string.length() izracunava broj znamenki i na temelju broj * znamenki odreduje mjernu jedinicu. Odreduje se najmanja promjena, te * se na temelju nje postavlja skala u najmanju jedinicu i mnozi ostatak s * odgovarajucim 10^x faktorom. * */ // FIXED: ovo ne radi ako citava simulacija traje 0 pa je i minimalna promjena 0! // Zato ova petlja ispod. // String minimumDuration = String.valueOf((long)temporaryDurations[0]); String minimumDuration = null; double firstNonZeroDuration = 0; for(double dur : temporaryDurations) { long ldur = (long)dur; if(ldur>0) { firstNonZeroDuration = dur; minimumDuration = String.valueOf(ldur); break; } } if(minimumDuration==null) { minimumDuration = "1"; firstNonZeroDuration = 1; } //System.out.println("minimalna razlika je " + minimumDuration); int numberOfEndZeroes = 0; char[] tempArray = minimumDuration.toCharArray(); for (int i = tempArray.length - 1; i >= 0; i--) { if (tempArray[i] == '0') { numberOfEndZeroes++; } else { break; } } switch (numberOfEndZeroes) { case 0 : case 1 : case 2 : measureUnitName = "fs"; measureUnit = FEMTO_SECONDS; break; case 3 : case 4 : case 5 : measureUnitName = "ps"; measureUnit = PICO_SECONDS; break; case 6 : case 7 : case 8 : measureUnitName = "ns"; measureUnit = NANO_SECONDS; break; case 9 : case 10 : case 11 : measureUnitName = "us"; measureUnit = MICRO_SECONDS; break; case 12 : case 13 : case 14 : measureUnitName = "ms"; measureUnit = MILI_SECONDS; break; case 15 : case 16 : case 17 : measureUnitName = "s"; measureUnit = SECONDS; break; } scaleEndPointInPixels = 0; // minimumDurationInTime = (int)(durationsInFemtoSeconds[0] * measureUnit); minimumDurationInTime = (int)(firstNonZeroDuration * measureUnit); for (int i = 0; i < durationsInTime.length; i++) { durationsInTime[i] = (int)(durationsInFemtoSeconds[i] * measureUnit); if (durationsInTime[i] < minimumDurationInTime) { if(durationsInTime[i]>0) minimumDurationInTime = durationsInTime[i]; } scaleEndPointInPixels += durationsInTime[i]; } scaleStepInTime = minimumDurationInTime; pixelFactor = 100 / scaleStepInTime; scaleEndPointInPixels *= pixelFactor; for (int i = 0; i < transitionPoints.length; i++) { transitionPoints[i] = (int)(transitionPoints[i] * measureUnit); } // izracunaj durationsInPixels for (int j = 0; j < durationsInPixels.length; j++) { durationsInPixels[j] = (int)(transitionPoints[j + 1] * pixelFactor) - (int)(transitionPoints[j] * pixelFactor); } } /** * Metoda koja se brine o trajanju piksela nakon event-hendlanja i o tocnom * postavljanju scaleEndPointInPixels */ public void setDurationsInPixelsAfterZoom (double scaleFactor) { scaleStepInTime /= scaleFactor; pixelFactor *= scaleFactor; scaleEndPointInPixels = 0; for (int i = 0; i < durationsInTime.length; i++) { /* s istim 10^x faktorom mnozi se cijelo vrijeme */ durationsInTime[i] = (int)(durationsInFemtoSeconds[i] * measureUnit); scaleEndPointInPixels += durationsInTime[i]; } scaleEndPointInPixels *= pixelFactor; // izracunaj durationsInPixels for (int i = 0; i < durationsInPixels.length; i++) { durationsInPixels[i] = (int)(transitionPoints[i + 1] * pixelFactor) - (int)(transitionPoints[i] * pixelFactor); } this.scaleFactor *= scaleFactor; } /** * Getter trajanje u pikselima za valne oblike */ public int[] getDurationInPixels() { return durationsInPixels; } /** * Setter postavlja scaleFactor * * @param scaleFactor Zeljeni faktor */ public void setScaleFactor (float scaleFactor) { this.scaleFactor = scaleFactor; } /** * Vraca trenutni scale faktor */ public double getScaleFactor () { return scaleFactor; } /** * Getter koji je potreban event-hendleru za precizno oznacavanje pozicije */ public String getMeasureUnitName () { return measureUnitName; } /** * Setter koji postavlja horizontalni offset u ovisnosti i scrollbaru * * @param offset Zeljeni offset */ public void setHorizontalOffset (int offset) { this.offsetXAxis = offset; } /** * Trenutni horizontalni offset */ public int getHorizontalOffset () { return offsetXAxis; } /** * Getter koji daje krajnju tocku u pikselima, potreban za postavljanje nove * vrijednosti scrollbara prilikom povecanja/smanjenja */ public int getScaleEndPointInPixels () { return scaleEndPointInPixels; } /** * Getter koji daje trenutnu vrijednost skale u vremenu (npr. 345 ns na 100 * piksela) */ public double getScaleStepInTime () { return scaleStepInTime; } /** * Preferirane dimenzije */ @Override public Dimension getPreferredSize () { return new Dimension(scaleEndPointInPixels, SCALE_HEIGHT); } /** * Zumiraj tako da sve stane u jedan prozor */ public void fitToWindow() { scaleFactor = (double)800 / scaleEndPointInPixels; setDurationsInPixelsAfterZoom(scaleFactor); } /** * Metoda vraca u defaultno stanje */ public void unfitToWindow() { scaleFactor = 1; scaleStepInTime = minimumDurationInTime; pixelFactor = 100 / scaleStepInTime; scaleEndPointInPixels *= pixelFactor; setDurationsInPixelsAfterZoom(scaleFactor); // System.out.println("da"); } /** * Crtanje komponente */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(themeColor.getScale()); g.setColor(themeColor.getLetters()); /* postavlja novu vrijednost scrollbara */ int horizontalValue = this.scaleEndPointInPixels - this.getWidth() + 45; if (horizontalValue >= 0) { horizontalScrollbar.setMaximum(horizontalValue); } /* * Bilo koja vrijednost postavlja se na visekratnik broja 100. Znaci, ako je * offset bio 1450, stavit ce 1400 i poceti crtati od 1400 */ screenStartPointInPixels = offsetXAxis - offsetXAxis % 100; /* * Duljina trenutno aktivnog ekrana koji ce se iscrtati je duljina panela + 200 * Skala se ne crta kompletno, vec samo dio koji trenutno upada u offset */ screenSizeInPixels = getWidth() + 200; screenEndPointInPixels = screenStartPointInPixels + screenSizeInPixels; scaleFactor = Math.round(scaleFactor * 1e13d) / 1e13d; /* scaleStepInTime ostaje fiksan jer je potrebna pocetna vrijednost */ double tempScaleStepInTime = scaleStepInTime; /* x se mijenja u koracima od 100 piksela */ int x = 0; /* * samo ako je screenStartPointInPixels == 0 npr offset = 30, crta od * nultog piksela, ali pomatnut ulijevo za offset, pa 30 pocinje od * nultog piksela */ if (screenStartPointInPixels == SCALE_START_POINT_IN_PIXELS) { g.drawLine(x - offsetXAxis, SCALE_TAG_LINE_YSTART, x - offsetXAxis, SCALE_TAG_LINE_YEND); g.drawString("0", x - offsetXAxis, SCALE_VALUE_YAXIS); x = SCALE_STEP_IN_PIXELS; scaleValue = scaleStepInTime; } /* * inace moze biti npr. 2500. scaleValue se odmah postavlja na pocetnu * vrijednost u skladu s brojem od kojeg pocinje crtanje */ else { x = screenStartPointInPixels; scaleValue = screenStartPointInPixels / 100 * scaleStepInTime; } int endPoint = screenEndPointInPixels; g.drawLine(screenStartPointInPixels - offsetXAxis, SCALE_MAIN_LINE_YAXIS, endPoint - offsetXAxis, SCALE_MAIN_LINE_YAXIS); String tempMeasureUnitName = measureUnitName; int endPointInPixels = endPoint; double potention = 1; while (x < endPointInPixels) { /* svaka se vrijednost zaokruzuje na 10 decimala */ scaleValue = Math.round(scaleValue * 1e13d) / 1e13d; /* * ako vrijednost prijede 1000, prebacuje se na sljedecu vecu * jedinicu i ujedno smanjuje scaleStepInTime za 1000 */ if (scaleValue >= 1000) { for (int i = 0; i < units.length; i++) { if (units[i].equals(tempMeasureUnitName) && i < units.length - 1) { tempMeasureUnitName = units[i + 1]; scaleValue /= 1000.0; tempScaleStepInTime /= 1000; potention *= 1000; break; } } } /* inace ako prijede u manju jedinicu */ else if (scaleValue < 1 && scaleValue != 0) { for (int i = 0; i < units.length; i++) { if (units[i].equals(tempMeasureUnitName) && i > 0) { tempMeasureUnitName = units[i - 1]; scaleValue *= 1000; tempScaleStepInTime *= 1000; potention /= 1000; break; } } } /* * bez obzira na vrijednost x, sve se crta pomaknuto za offset, tako * da na ekranu bude slika od nultog piksela */ g.drawLine(x - offsetXAxis, SCALE_TAG_LINE_YSTART, x - offsetXAxis, SCALE_TAG_LINE_YEND); g.drawString((Math.round(scaleValue * 1000000d) / 1000000.0d) + tempMeasureUnitName, x -(((Math.round(scaleValue * 1000000d) / 1000000.0d) + tempMeasureUnitName).length() * 5) / 2 - offsetXAxis, SCALE_VALUE_YAXIS); scaleValue += tempScaleStepInTime; // System.out.println("pixelFactor " + pixelFactor); // System.out.println("potention " + potention); x = (int)(scaleValue * potention * pixelFactor); } } }