All Downloads are FREE. Search and download functionalities are using the official Maven repository.

acm.graphics.GPen Maven / Gradle / Ivy

Go to download

This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf

The newest version!
/*
 * @(#)GPen.java   1.99.1 08/12/08
 */

// ************************************************************************
// * Copyright (c) 2008 by the Association for Computing Machinery        *
// *                                                                      *
// * The Java Task Force seeks to impose few restrictions on the use of   *
// * these packages so that users have as much freedom as possible to     *
// * use this software in constructive ways and can make the benefits of  *
// * that work available to others.  In view of the legal complexities    *
// * of software development, however, it is essential for the ACM to     *
// * maintain its copyright to guard against attempts by others to        *
// * claim ownership rights.  The full text of the JTF Software License   *
// * is available at the following URL:                                   *
// *                                                                      *
// *          http://www.acm.org/jtf/jtf-software-license.pdf             *
// *                                                                      *
// ************************************************************************

// REVISION HISTORY
//
// -- V2.0 --
// Documentation fix 26-Apr-07 (ESR)
//   1. Removed inherited method description for setLocation.
//
// Code cleanup 28-May-07 (ESR)
//   1. Added generic type tags.
//
// Feature enhancement 26-May-08 (ESR)
//   1. Added support for serialization.

package acm.graphics;

import acm.util.*;
import java.awt.*;
import java.io.*;
import java.util.*;

/* Class: GPen */
/**
 * The GPen class simulates a pen drawing on a canvas.
 * As with the other graphical objects in the
 * acm.graphics
 * package, you use this class by constructing a new GPen
 * object and adding that object to a GCanvas.
 * You can move the pen on the canvas by using the methods
 * setLocation and
 * move and then
 * draw lines using  drawLine.
 * The GPen object remembers the series of lines
 * and can therefore repaint the screen image when necessary.
 */
public class GPen extends GObject {

/* Constructor: GPen() */
/**
 * Creates a new GPen object with an empty path.
 *
 * Example: GPen gpen = new GPen();
 */
	public GPen() {
		penVisible = false;
		path = new PathList();
		animator = new Animator();
		setSpeed(1.0);
		erasePath();
	}

/* Constructor: GPen(x, y) */
/**
 * Creates a new GPen object with an empty path, initially
 * positioned at the point (x, y).
 *
 * Example: GPen gpen = new GPen(x, y);
 * @param x The initial x coordinate of the pen
 * @param y The initial y coordinate of the pen
 */
	public GPen(double x, double y) {
		this();
		setLocation(x, y);
	}

/* Method: erasePath() */
/**
 * Erases the entire path drawn by the pen but does not change its position.
 *
 * Example: gpen.erasePath();
 */
	public void erasePath() {
		path.clear();
		xScale = 1.0;
		yScale = 1.0;
		regionOpen = false;
		regionStarted = false;
		repaint();
	}

/* Method: setLocation(x, y) */
/**
 * Moves the pen to the point (x, y) without drawing a line.
 *
 * Example: gpen.setLocation(x, y);
 * @param x The x-coordinate of the new position
 * @param y The y-coordinate of the new position
 */
	public void setLocation(double x, double y) {
		if (regionStarted) {
			throw new ErrorException("It is illegal to move the pen while you are " +
			                         "defining a filled region.");
		}
		super.setLocation(x, y);
		animator.delay();
	}

/* Method: drawLine(dx, dy) */
/**
 * Draws a line segment with displacements dx and dy.
 * Drawing a line leaves the pen positioned at the end of the line segment, so
 * that the next call to drawLine will continue from where this
 * one finished.
 *
 * Example: drawLine(dx, dy);
 * @param dx The extent of the line in the x direction
 * @param dy The extent of the line in the y direction
 */
	public void drawLine(double dx, double dy) {
		double x = getX();
		double y = getY();
		if (regionStarted) {
			path.add(new DrawLineElement(dx, dy));
		} else {
			path.add(new SetLocationElement(x, y), new DrawLineElement(dx, dy));
			regionStarted = regionOpen;
		}
		super.setLocation(x + dx, y + dy);
		animator.delay();
	}

/* Method: drawPolarLine(r, theta) */
/**
 * Draws a line segment using displacements given in polar coordinates.
 * The parameter r specifies the length of the line segment, and
 * theta specifies the angle at which the line is drawn.  The
 * angle is measured in degrees increasing counterclockwise from the +x axis.
 * Thus, the call
 *
 *
 *      drawPolarLine(1.0, 0);
 *
 *
 * 

draws a line extending rightward one inch from the current point. * Similarly * * *   drawPolarLine(2.0, 135); * * *

draws a two-inch line extending to the northwest from the current point. * * Example: drawPolarLine(r, theta); * @param r The length of the line segment * @param theta The angle at which to draw the line, measured in degrees * increasing counterclockwise from the +x axis */ public final void drawPolarLine(double r, double theta) { double radians = theta * Math.PI / 180; drawLine(r * Math.cos(radians), -r * Math.sin(radians)); } /* Method: setColor(color) */ /** * Sets the color of the pen. * * Example: gpen.setColor(color); * @param color The new color for the pen */ public void setColor(Color color) { if (regionStarted) { throw new ErrorException("It is illegal to change the color while you are " + "defining a filled region."); } path.add(new SetColorElement(color)); super.setColor(color); } /* Method: setFillColor(color) */ /** * Sets the color used to fill a region. * * Example: gpen.setFillColor(color); * @param color The new color used to fill a region */ public void setFillColor(Color color) { if (regionStarted) { throw new ErrorException("It is illegal to change the fill color while you are " + "defining a filled region."); } fillColor = color; } /* Method: getFillColor() */ /** * Returns the color used to display the filled region of this object. If * none has been set, getFillColor returns the color of the * object. * * Example: Color color = gpen.getFillColor(); * @return The color used to display the filled region of this object */ public Color getFillColor() { return (fillColor == null) ? getColor() : fillColor; } /* Method: startFilledRegion() */ /** * Starts defining a region that will be filled with the current color. * Any line segments drawn between now and the matching call to * endFilledRegion will be used to create a polygon that * is then filled. * * Example: gpen.startFilledRegion(); */ public void startFilledRegion() { if (regionOpen) { throw new ErrorException("You are already filling a region."); } regionOpen = true; regionStarted = false; path.add(new StartRegionElement(fillColor)); } /* Method: endFilledRegion() */ /** * Ends the definition of a region and fills it with the current color. * * Example: gpen.endFilledRegion(); */ public void endFilledRegion() { if (!regionOpen) { throw new ErrorException("You need to call startFilledRegion " + "before you call endFilledRegion."); } regionOpen = false; regionStarted = false; path.add(new EndRegionElement()); repaint(); } /* Method: showPen() */ /** * Makes the pen itself visible. If the pen is visible, the drawPen * method will be called at the end of painting the path to show the current location. * * Example: gpen.showPen(); */ public void showPen() { penVisible = true; repaint(); animator.delay(); } /* Method: hidePen() */ /** * Makes the pen itself invisible. * * Example: gpen.showPen(); */ public void hidePen() { penVisible = false; repaint(); animator.delay(); } /* Method: isPenVisible() */ /** * Returns whether the pen is visible. * * Example: if (isPenVisible(visible)) . . . * @return true if the pen is visible, otherwise false */ public boolean isPenVisible() { return penVisible; } /* Method: setSpeed(speed) */ /** * Sets the speed of the pen, which must be a number between 0 (slowest) * and 1 (fastest). Setting speed to a value less than one makes the pen * move slowly, thereby making it easy to see exactly how a figure is being drawn. * * Example: setSpeed(speed); * @param speed The speed of the pen (0 is slowest, 1 is fastest) */ public void setSpeed(double speed) { animator.setSpeed(speed); } /* Method: getSpeed() */ /** * Returns the current speed of the pen. * * Example: double speed = getSpeed(); * @return The current speed of the pen (0 is slowest, 1 is fastest) */ public double getSpeed() { return animator.getSpeed(); } /* Method: scale(sx, sy) */ /** * Sets the scale factors sx and sy. Scaling a GPen * object changes the location of points as well the size of line segments. This * behavior seems counterintuitive for a pen installed directly in a GCanvas, * but is quite useful when a GPen is embedded in a compound object. * * Example: gpen.scale(sx, sy); * @param sx The factor used to scale all coordinates in the x direction * @param sy The factor used to scale all coordinates in the y direction */ public void scale(double sx, double sy) { xScale = sx; yScale = sy; repaint(); } /* Method: paint(g) */ /** * Implements the paint operation for this graphical object. This method * is not called directly by clients. * */ public void paint(Graphics g) { PathState state = new PathState(); state.sx = xScale; state.sy = yScale; path.mapPaint(g, state); if (penVisible) drawPen(g); } /* Method: getBounds() */ /** * Returns the bounding box for the entire figure traced by the pen. * * Example: bounds = getBounds(); * @return A GRectangle representing the bounding box */ public GRectangle getBounds() { PathState state = new PathState(); state.sx = xScale; state.sy = yScale; return path.getBounds(state); } /* Method: contains(x, y) */ /** * Contains is defined to be false for the GPen object to avoid having the * trace intercept mouse clicks. * * */ public boolean contains(double x, double y) { return false; } /* Method: setPenImage(image) */ /** * Sets the image of the pen to be the specified image. This image is drawn with * its center at the pen position. * * Example: pen.setPenImage(image); * @param image The new image to use for the pen */ public void setPenImage(Image image) { penImage = MediaTools.loadImage(image); } /* Method: getPenImage() */ /** * Returns the image used to draw the pen when setPenVisible has been * called. * * @return Image pen image */ public Image getPenImage() { if (penImage == null) penImage = PenImage.getImage(); return penImage; } /* Inherited method: getLocation() */ /** * @inherited GObject#GPoint getLocation() * Returns the location of this object as a GPoint. */ /* Inherited method: getX() */ /** * @inherited GObject#double getX() * Returns the x-coordinate of the object. */ /* Inherited method: getY() */ /** * @inherited GObject#double getY() * Returns the y-coordinate of the object. */ /* Inherited method: getSize() */ /** * @inherited GObject#GDimension getSize() * Returns the size of the bounding box for this object. */ /* Inherited method: getWidth() */ /** * @inherited GObject#double getWidth() * Returns the width of this object as a double-precision value, which * is defined to be the width of the bounding box. */ /* Inherited method: getHeight() */ /** * @inherited GObject#double getHeight() * Returns the height of this object as a double-precision value, which * is defined to be the height of the bounding box. */ /* Inherited method: move(dx, dy) */ /** * @inherited GObject#void move(double dx, double dy) * Moves the pen on the screen by the specified displacements without drawing a line. */ /* Inherited method: movePolar(r, theta) */ /** * @inherited GObject#void movePolar(double r, double theta) * Moves the pen using displacements given in polar coordinates without drawing * a line. */ /* Inherited method: sendToFront() */ /** * @inherited GObject#void sendToFront() * Moves this object to the front of the display in the z dimension. */ /* Inherited method: sendToBack() */ /** * @inherited GObject#void sendToBack() * Moves this object to the back of the display in the z dimension. */ /* Inherited method: sendForward() */ /** * @inherited GObject#void sendForward() * Moves this object one step toward the front in the z dimension. */ /* Inherited method: sendBackward() */ /** * @inherited GObject#void sendBackward() * Moves this object one step toward the back in the z dimension. */ /* Inherited method: getColor() */ /** * @inherited GObject#Color getColor() * Returns the color used to display this object. */ /* Inherited method: setVisible(visible) */ /** * @inherited GObject#void setVisible(boolean visible) * Sets whether the trace of the pen is visible. */ /* Inherited method: isVisible() */ /** * @inherited GObject#boolean isVisible() * Checks to see whether the trace of the pen is visible. */ /* Protected method: drawPen(g) */ /** * This method draws a representation of the pen at the current location. * Subclasses can override this method to draw fancier pens. * * Example: drawPen(g); * @param g The graphics context in which to draw the pen */ protected void drawPen(Graphics g) { Component comp = getComponent(); if (comp == null) return; if (penImage == null) penImage = PenImage.getImage(); int width = penImage.getWidth(comp); int height = penImage.getHeight(comp); int x = (int) Math.round(getX()); int y = (int) Math.round(getY()); g.drawImage(penImage, x - width / 2, y - height / 2, comp); } /* Protected method: getPenBounds() */ /** * @return This method returns the bounds that the pen occupies. * * Example: Rectangle r = getPenBounds(); */ protected Rectangle getPenBounds() { Component comp = getComponent(); if (comp == null) return new Rectangle(); if (penImage == null) penImage = PenImage.getImage(); int width = penImage.getWidth(comp); int height = penImage.getHeight(comp); int x = (int) Math.round(getX()); int y = (int) Math.round(getY()); return new Rectangle(x - width / 2, y - height / 2, width, height); } /* Private instance variables */ private Animator animator; private double xScale, yScale; private boolean regionOpen; private boolean regionStarted; private boolean penVisible; private PathList path; private Image penImage; private Color fillColor; /* Serial version UID */ /** * The serialization code for this class. This value should be incremented * whenever you change the structure of this class in an incompatible way, * typically by adding a new instance variable. */ static final long serialVersionUID = 1L; } /* Package class: PathList */ /** * The PathList class represents a list of path elements. */ class PathList implements Serializable { /* Constructor: new PathList() */ /** * Creates a new PathList with no elements. */ public PathList() { path = new ArrayList(); } /* Method: add(element) */ /** * Adds the specified path element to the end of the contents list. */ public synchronized void add(PathElement element) { path.add(element); } /* Method: add(e1, e2) */ /** * Adds two path elements under a single synchronization lock. */ public synchronized void add(PathElement e1, PathElement e2) { path.add(e1); path.add(e2); } /* Method: remove(element) */ /** * Removes the specified element from the list. */ public synchronized void remove(PathElement element) { path.remove(element); } /* Method: clear() */ /** * Removes all path elements from the list. */ public synchronized void clear() { path.clear(); } /* Method: getElementCount() */ /** * @return Returns the number of path elements in the list. */ public int getElementCount() { return path.size(); } /* Method: getElement(index) */ /** * @return Returns the path element at the specified index. */ public PathElement getElement(int index) { return path.get(index); } /* Method: getBounds() */ /** * @return Returns the bounding rectangle for the objects in the list. */ public synchronized GRectangle getBounds(PathState state) { GRectangle bounds = new GRectangle(-1, -1, -1, -1); int nElements = path.size(); for (int i = 0; i < nElements; i++) { PathElement element = path.get(i); element.updateBounds(bounds, state); } return bounds; } /* Method: mapPaint(g, state) */ /** * Paints all the elements of the path using the graphics context g. */ public synchronized void mapPaint(Graphics g, PathState state) { int nElements = path.size(); for (int i = 0; i < nElements; i++) { PathElement element = path.get(i); element.paint(g, state); } FINAL_PATH_ELEMENT.paint(g, state); } /* Private constants */ private static final PathElement FINAL_PATH_ELEMENT = new FinalPathElement(); /* Private instance variables */ private ArrayList path; }; /* Package class: PathState */ /** * The PathState class maintains the information necessary to * render the path. It is a structure in which the elements are read directly * by the PathElement subclasses. */ class PathState implements Serializable { double cx, cy; /* The current pen position, before scaling */ double sx, sy; /* The current scale factors, applied before rendering */ Polygon region; /* The current region, or null if no region is in effect */ Color fillColor; /* The color used to fill the region */ }; /* Package abstract class: PathElement */ /** * The PathElement class is used to elements of the path being constructed. * PathElement is the abstract class; the actual code to render and * compute the bounds of the path element is provided by the concrete classes. */ abstract class PathElement implements Serializable { /* Constructor: PathElement() */ /** * The default constructor for this class. */ public PathElement() { /* Empty */ } /* Method: paint(g, state); */ /** * Performs whatever functions are necessary when this path element is * encountered during a painting traversal. */ public void paint(Graphics g, PathState state) { if (g == g) /* Avoid unused parameter warning */; if (state == state) /* Avoid unused parameter warning */; } /* Method: updateBounds(bounds, state); */ /** * Updates the bounds of the rectangle as it would if this operation were * performed. */ public void updateBounds(GRectangle bounds, PathState state) { if (bounds == bounds) /* Avoid unused parameter warning */; if (state == state) /* Avoid unused parameter warning */; } } /* Package class: SetLocationElement */ /** * The SetLocationElement class is used to represent a change in * position. */ class SetLocationElement extends PathElement { public SetLocationElement(double x, double y) { cx = x; cy = y; } public void paint(Graphics g, PathState state) { state.cx = cx; state.cy = cy; if (state.region != null) { state.region.addPoint(GMath.round(state.sx * cx), GMath.round(state.sy * cy)); } } public void updateBounds(GRectangle bounds, PathState state) { state.cx = cx; state.cy = cy; } private double cx, cy; } /* Package class: DrawLineElement */ /** * The DrawLineElement class is used to represent a * line. */ class DrawLineElement extends PathElement { public DrawLineElement(double dx, double dy) { deltaX = dx; deltaY = dy; } public void paint(Graphics g, PathState state) { int x0 = GMath.round(state.sx * state.cx); int y0 = GMath.round(state.sy * state.cy); state.cx += deltaX; state.cy += deltaY; int x1 = GMath.round(state.sx * state.cx); int y1 = GMath.round(state.sy * state.cy); if (state.region == null) { g.drawLine(x0, y0, x1, y1); } else { state.region.addPoint(x1, y1); } } public void updateBounds(GRectangle bounds, PathState state) { if (bounds.getWidth() < 0) { bounds.setBounds(state.sx * state.cx, state.sy * state.cy, 0, 0); } else { bounds.add(state.sx * state.cx, state.sy * state.cy); } state.cx += deltaX; state.cy += deltaY; bounds.add(state.sx * state.cx, state.sy * state.cy); } private double deltaX, deltaY; } /* Package class: SetColorElement */ /** * The SetColorElement class is used to represent a * change of color. */ class SetColorElement extends PathElement { public SetColorElement(Color color) { myColor = color; } public void paint(Graphics g, PathState state) { g.setColor(myColor); } private Color myColor; } /* Package class: StartRegionElement */ /** * The StartRegionElement class marks the beginning of a region. */ class StartRegionElement extends PathElement { public StartRegionElement(Color color) { myColor = color; } public void paint(Graphics g, PathState state) { state.region = new Polygon(); state.fillColor = myColor; } private Color myColor; } /* Package class: EndRegionElement */ /** * The EndRegionElement class marks the end of a region. */ class EndRegionElement extends PathElement { public EndRegionElement() { /* Empty */ } public void paint(Graphics g, PathState state) { Color oldColor = g.getColor(); g.setColor(state.fillColor); g.fillPolygon(state.region.xpoints, state.region.ypoints, state.region.npoints); g.setColor(oldColor); g.drawPolygon(state.region.xpoints, state.region.ypoints, state.region.npoints); state.region = null; } } /* Package class: FinalPathElement */ /** * The FinalPathElement class is invoked at the end of the path * and has the effect of drawing an incomplete region. */ class FinalPathElement extends PathElement { public FinalPathElement() { /* Empty */ } public void paint(Graphics g, PathState state) { if (state.region != null) { g.drawPolyline(state.region.xpoints, state.region.ypoints, state.region.npoints); } } } /* Package class: PenImage */ /** * This class encapsulates the image used to display the pen. */ class PenImage { public static Image getImage() { return MediaTools.createImage(HEX_DATA); } private static final String[] HEX_DATA = { "4749463839614F006500F70000FFFFFF980098339999989800111111222222000054CBFFCB003298", "0033660033CC0033FE00323266330066660000659800989800CC9900FE99329800659800CC0099FE", "0098659898999999CC9900FE98009800329800659900CC9800FE3399CB3399FF9999339898659832", "0098650099339998659833CB9833FF9999CC0099FE00336699656698CC9898FF9999323200336600", "32003233006632009833339965009866339900663300983200666600986500CC3300FE3200CC6600", "FE65CCCC98CCFF99FFCC99FFFF993300CC3200FE6600CC6500FECC0033CC0066FE0032FE00653399", "33339966669933669865CC00CCCB00FEFE00CBFE00FE6699CC6598FF9898CC9999FFCB9833CC9966", "FF9933FF9865333333326532323265326565660033653232660066653265CC3300CC6600FE3200FE", "65000066CC0099CC0066FE0098FE00CCCC00FECB00CCFE00FEFE33CC0033FE0066CC0066FE00CB33", "98CC6699FF3399FF659866CC9965FF9898CC9899FF99CCCC00CCFE00FECB00FEFE00993333996633", "9933669865659833CB9966CC9933FF9865FF33CBCB33FFCC33CCFF33FFFF99CB3399FF3399CC6698", "FF65CC98CCCCCCCCCC99FFCBCBFFFF99CCFFCBCBFF99FFFFCBFF3333CB3366CB3333FF3366FF6533", "CB6666CC6633FF6565FFCB3333CB6533CB3365CC6666FF3333FF6633FF3366FF656533CB3333FF33", "33CB6633FF6666CB3366FF3366CC6665FF65CB33CBCC66CCCC33FFCC65FFFF33CCFF65CCFF33FFFF", "65FF66CCCC65FFCC65CCFF65FFFF98CCCC99FFCC99CCFF99FFFFCBCB33CCFF33CCCC66CCFF65FFCC", "33FFFF33FFCC65FFFF65444444656532DDDDDDCBFFFFFFFFCBEEEEEE100000980000001000660000", "000098000066777777888888AAAAAABBBBBB5555556666660000100000224400005400000000CC00", "00DC0000EE0000FE00003200004400880000980000AA0000BA0000CC0000DC0000EE0000FE00CC00", "00DC0000EE0000FE0000004400005400006600007600220000320000AA0000BA0000002200003200", "7600008800000000AA0000BA00007600008800000021F90401000000002C000000004F006500C7FF", "FFFF980098339999989800111111222222000054CBFFCB0032980033660033CC0033FE0032326633", "0066660000659800989800CC9900FE99329800659800CC0099FE0098659898999999CC9900FE9800", "9800329800659900CC9800FE3399CB3399FF99993398986598320098650099339998659833CB9833", "FF9999CC0099FE00336699656698CC9898FF99993232003366003200323300663200983333996500", "9866339900663300983200666600986500CC3300FE3200CC6600FE65CCCC98CCFF99FFCC99FFFF99", "3300CC3200FE6600CC6500FECC0033CC0066FE0032FE0065339933339966669933669865CC00CCCB", "00FEFE00CBFE00FE6699CC6598FF9898CC9999FFCB9833CC9966FF9933FF98653333333265323232", "65326565660033653232660066653265CC3300CC6600FE3200FE65000066CC0099CC0066FE0098FE", "00CCCC00FECB00CCFE00FEFE33CC0033FE0066CC0066FE00CB3398CC6699FF3399FF659866CC9965", "FF9898CC9899FF99CCCC00CCFE00FECB00FEFE009933339966339933669865659833CB9966CC9933", "FF9865FF33CBCB33FFCC33CCFF33FFFF99CB3399FF3399CC6698FF65CC98CCCCCCCCCC99FFCBCBFF", "FF99CCFFCBCBFF99FFFFCBFF3333CB3366CB3333FF3366FF6533CB6666CC6633FF6565FFCB3333CB", "6533CB3365CC6666FF3333FF6633FF3366FF656533CB3333FF3333CB6633FF6666CB3366FF3366CC", "6665FF65CB33CBCC66CCCC33FFCC65FFFF33CCFF65CCFF33FFFF65FF66CCCC65FFCC65CCFF65FFFF", "98CCCC99FFCC99CCFF99FFFFCBCB33CCFF33CCCC66CCFF65FFCC33FFFF33FFCC65FFFF6544444465", "6532DDDDDDCBFFFFFFFFCBEEEEEE100000980000001000660000000098000066777777888888AAAA", "AABBBBBB5555556666660000100000224400005400000000CC0000DC0000EE0000FE000032000044", "00880000980000AA0000BA0000CC0000DC0000EE0000FE00CC0000DC0000EE0000FE000000440000", "5400006600007600220000320000AA0000BA00000022000032007600008800000000AA0000BA0000", "7600008800000008FF0001081C48B0A0C18308132A5CA81012248610234A640809DBBF2DD830E431", "D1451B868920432684C4022306483F5AB0E882ADE5968F226386C40026E38F479030E8C45072CB0F", "9940252EEB226265476D0CFE29C5F630A8538818B6B4C00869D9D3AB22B7FC636115ABD7AF60C38A", "1D4BB6ACD9B368D3AA5DCB166B450334E23280D916E48F2D1E7F406AA11792096D2CEA4A848491C5", "8FBE3FF23CFA81C10B5DC10A317089CAC221DF9C5E5E428EF8A30B06315D0C30D0267573C8652CA2", "3E36CDBAB5EBD7B063CB9E4DBBB6EDC1D80C6CD9D2F4B6418E09BAD0B04143B3EF81183A62435CA5", "4B8CC0C73B6364BCF3824E9FBE519BD49B27A76516CB6F57FFC4963AE70A315EC0E0DD52597C4B9D", "7A5B3844597235ED962CE2D2E8D245065E6C941C0700249E61D3C50CA46504C901020A74D7671D79", "D15B830E7A661C850661808D18D860E8E187208628E288249668E28928A6A8E28A6C2D13156F2326", "275C0C4C819887180CE8C7D28402A2C6400C5BD8E0455C5D74D5E00F167551194A5EF0C7608384BD", "601824BBC8971C76024615C305F275F74877D878F1D37184B177521E3AB160020BE859729C760A22", "F6C32E4876C1236D5191E7D04D7BE6D4451E6416261F75D571E8E66DA8BDA7174F8CB2B09591B60D", "95D1231718888D132C8077E16D26607381097ACD199F81F6D1462084CFB17041A65B743126996068", "5728DC51EBDD699B8618C4B5C58F5B00D95E83B8CEFADF931882975C71A086F81E035B98606B8382", "8057638C196D11E088E3C148A2815892F8CFB42C862BEEB8E4966BEEB9E8A6ABEEBAECB6EBEEBBF0", "C62BEFBCF4CA1B100021FF0B4D414347436F6E2004031039000000015772697474656E2062792047", "4946436F6E76657274657220322E342E33206F66204D6F6E6461792C204D61792032352C20313939", "38003B" }; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy