Package edu.mit.blocks.codeblocks

Source Code of edu.mit.blocks.codeblocks.InfixBlockShape

package edu.mit.blocks.codeblocks;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import edu.mit.blocks.renderable.RenderableBlock;

import edu.mit.blocks.codeblocks.rendering.BlockShapeUtil;

/**
* Specifies the BlockShape of infix blocks.  Infix blocks have two bottom sockets. 
*/
public class InfixBlockShape extends BlockShape {

    /**
     * In order to properly determine where the pen should be after drawing all the blocks, the variable
     * maxX is used. This stores the maximum x-coordinate visited by the pen and is updated
     * after each connected block is drawn. After the update, the pen is moved to the maximum
     * x-coordinate visited. When finished drawing all connected blocks, maxX is reset to 0 to
     * allow for resizing if some of the connected blocks are removed.
     */
    private float maxX = 0; //Stores the maximum X-coordinate visited by the infix block

    public InfixBlockShape(RenderableBlock rb) {
        super(rb);
    }

    /**
     * Overrided from BlockShape.
     * Takes into account the need to resize the dimensions of an infix block for various cases.
     */
    @Override
    protected void makeBottomSide() {
        // Reset the maximum X-coordinate so the infix block can resize if you remove blocks within it
        maxX = 0;

        //start bottom-right
        setEndPoint(gpBottom, botLeftCorner, topLeftCorner, true);

        //curve down and right
        BlockShapeUtil.cornerTo(gpBottom, botLeftCorner, botRightCorner, blockCornerRadius);



        /// BOTTOM SOCKETS
        //for each socket in the iterator
        int socketCounter = 0; //need to use this to determine which socket we're on
        for (BlockConnector curSocket : block.getSockets()) {

            //if bottom socket
            if (curSocket.getPositionType() == BlockConnector.PositionType.BOTTOM) {

                //move away from bottom left corner
                if (socketCounter > 0) {
                    gpBottom.lineTo(
                            (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_MIDDLE_SPACER,
                            (float) gpBottom.getCurrentPoint().getY());
                } else {
                    gpBottom.lineTo(
                            (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_SIDE_SPACER,
                            (float) gpBottom.getCurrentPoint().getY());
                }


                //move down so bevel doesn't screw up from connecting infinitely sharp corner
                // as occurs from a curved port
                BlockShapeUtil.lineToRelative(gpBottom, 0, -0.1f);

                //////////////////////
                //begin drawing socket
                //////////////////////

                if (curSocket.getBlockID() == Block.NULL) {
                    //draw first socket - up left side
                    Point2D leftSocket = BCS.addDataSocketUp(gpBottom, curSocket.getKind(), true);
                    rb.updateSocketPoint(curSocket, leftSocket);
                    //System.out.println("socket poitn: "+rb.getSocketPoint(curSocket));

                    //System.out.println("socket poitn leftsocket: "+leftSocket);

                    //draw left standard empty socket space - top side
                    gpBottom.lineTo(
                            (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_SIDE_SPACER,
                            (float) gpBottom.getCurrentPoint().getY());


                    //draw first socket - down right side
                    BCS.addDataSocket(gpBottom, curSocket.getKind(), false);
                    //rb.updateSocketPoint(curSocket, rightSocket);
                } else { //there is a connected block
                    Block connectedBlock = rb.getWorkspace().getEnv().getBlock(curSocket.getBlockID());
                    RenderableBlock connectedRBlock = rb.getWorkspace().getEnv().getRenderableBlock(curSocket.getBlockID());

                    //calculate and update the new socket point
                    //update the socket point of this cursocket which should now adopt the plug socket point of its
                    //connected block since we're also adopting the left side of its shape

                    //Use coordinates when the zoom level is 1.0 to calculate socket point
                    double unzoomX = connectedRBlock.getSocketPixelPoint(connectedBlock.getPlug()).getX() / connectedRBlock.getZoom();
                    double unzoomY = connectedRBlock.getSocketPixelPoint(connectedBlock.getPlug()).getY() / connectedRBlock.getZoom();
                    Point2D connectedBlockSocketPoint = new Point2D.Double(unzoomX, unzoomY);
                    Point2D currentPoint = gpBottom.getCurrentPoint();
                    double newX = connectedBlockSocketPoint.getX() + Math.abs(connectedBlockSocketPoint.getX() - currentPoint.getX());
                    double newY = connectedBlockSocketPoint.getY() + Math.abs(connectedRBlock.getBlockHeight() / connectedRBlock.getZoom() - currentPoint.getY());
                    rb.updateSocketPoint(curSocket, new Point2D.Double(newX, newY));


                    BlockShape connectedBlockShape = rb.getWorkspace().getEnv().getRenderableBlock(curSocket.getBlockID()).getBlockShape();
                    //append left side of connected block
                    appendPath(gpBottom, connectedBlockShape.getLeftSide(), false);




                    //append right side of connected block (more complicated)
                    if (connectedBlock.getNumSockets() == 0 || connectedBlock.isInfix()) {
//                      append top side of connected block
                        appendPath(gpBottom, connectedBlockShape.getTopSide(), false);
                        appendPath(gpBottom, connectedBlockShape.getRightSide(), false);
                    } else {
                        //iterate through the sockets of the connected block, checking if
                        //it has blocks connected to them
                        appendRightSidePath(gpBottom, connectedBlock, connectedBlockShape);
                    }

                    // Updates the maximum X-coordinate and sets the current point to maxX
                    if (maxX < (float) gpBottom.getCurrentPoint().getX()) {
                        maxX = (float) gpBottom.getCurrentPoint().getX();
                    }
                    gpBottom.lineTo(maxX, (float) gpBottom.getCurrentPoint().getY());


                }


                //bump down so bevel doesn't screw up
                BlockShapeUtil.lineToRelative(gpBottom, 0, 0.1f);

                //System.out.println("gpbottom starting point: "+gpBottom.getCurrentPoint());

                //// draw RIGHT to create divider ////
                if (socketCounter < block.getNumSockets() - 1) {
                    gpBottom.lineTo( //need to add the width of the block label.  warning: this assumes that there is only one block label
                            (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_MIDDLE_SPACER + rb.accomodateLabelsWidth(),
                            (float) gpBottom.getCurrentPoint().getY());
                } else {
                    gpBottom.lineTo(
                            (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_SIDE_SPACER,
                            (float) gpBottom.getCurrentPoint().getY());
                }

                socketCounter++;
            }
        }


        //curve right and up
        BlockShapeUtil.cornerTo(gpBottom, botRightCorner, topRightCorner, blockCornerRadius);

        //end bottom
        setEndPoint(gpBottom, botRightCorner, topRightCorner, false);
    }

    /**
     * Appends the right side path of the stack of blocks connected to the specified connectedBlock.  If there are
     * some empty sockets, this method will append empty placeholders. 
     * @param gpBottom the GeneralPath to append the new path to
     * @param connectedBlock the Block instance whose right side of its stack of connected blocks will be appened to the
     * specified gpBottom
     * @param connectedBlockShape the BlockShape of the specified connectedBlock
     */
    private void appendRightSidePath(GeneralPath gpBottom, Block connectedBlock, BlockShape connectedBlockShape) {

        //int lastBottomPathWidth;


        //append top side of connected block
        appendPath(gpBottom, connectedBlockShape.getTopSide(), false);

        float startX = (float) gpBottom.getCurrentPoint().getX();
        for (BlockConnector socket : connectedBlock.getSockets()) {
            // Sets the current x-coordinate to the start x-coordinate
            // Makes it so path movements created by previous blocks don't affect
            // the subsequent blocks.
            gpBottom.lineTo(startX, (float) gpBottom.getCurrentPoint().getY());
            if (socket.getBlockID() == Block.NULL) {
                //just draw an empty socket placeholder
                //if its the first socket, draw a top side
                gpBottom.lineTo(
                        (float) gpBottom.getCurrentPoint().getX() + BOTTOM_SOCKET_SIDE_SPACER,
                        (float) gpBottom.getCurrentPoint().getY());

                //now draw the empty right socket side
                //draw first socket - down right side
                BCS.addDataSocket(gpBottom, socket.getKind(), false);
                //TODO:lastBottomPathWidth = (int)BOTTOM_SOCKET_SIDE_SPACER;
            } else {
                //a block is connected to this socket, check if that block has sockets
                //OR if the block is an infix block - if it is infix, then just wrap around the infix block
                Block block = rb.getWorkspace().getEnv().getBlock(socket.getBlockID());
                BlockShape shape = rb.getWorkspace().getEnv().getRenderableBlock(socket.getBlockID()).getBlockShape();
                if (block.getNumSockets() == 0 || block.isInfix()) {
                    //append this block's top and right side
                    //TODO instead of just appending the right side...draw line to
                    appendPath(gpBottom, shape.getTopSide(), false);
                    appendPath(gpBottom, shape.getRightSide(), false);
                } else {
                    appendRightSidePath(gpBottom, block, shape);
                }
            }

            // Updates the maximum X-coordinate and sets the current point to maxX
            if (maxX < (float) gpBottom.getCurrentPoint().getX()) {
                maxX = (float) gpBottom.getCurrentPoint().getX();
            }
            gpBottom.lineTo(maxX, (float) gpBottom.getCurrentPoint().getY());
        }
    }

    /**
     * Overrided from BlockShape.
     * Determines the width of the sum of the bottom sockets and uses it if it is
     * greater than the width determined by the determineBlockWidth in BlockShape.
     * Else, it returns the sum of these two values.
     */
    @Override
    protected int determineBlockWidth() {

        //System.out.println("determining block width");

        int width = super.determineBlockWidth();

        //if the sum of bottom sockets is greater than the calculated width, then use it
        int bottomSocketWidth = 0;
        for (BlockConnector socket : block.getSockets()) {
            if (socket.getPositionType() == BlockConnector.PositionType.BOTTOM) {
                if (socket.getBlockID() == Block.NULL) {
                    //3 socket spacers = left of socket, between connectors, right of socket
                    bottomSocketWidth += BOTTOM_SOCKET_SIDE_SPACER;
                } else { //a block is connected to socket
                    //TODO get their assigned width from rb
                    if (rb.getSocketSpaceDimension(socket) != null) {
                        bottomSocketWidth += rb.getSocketSpaceDimension(socket).width;
                    }
                    bottomSocketWidth -= BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH;
                    // if it's a mirror plug, subtract for the other side, too.
                    if (rb.getWorkspace().getEnv().getBlock(socket.getBlockID()).getPlug().getPositionType() == BlockConnector.PositionType.MIRROR) {
                        bottomSocketWidth -= BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH;
                    }
                }
            }
        }


        bottomSocketWidth += 2 * BOTTOM_SOCKET_MIDDLE_SPACER;  //TODO need to decide for a size of the middle spacer and how to place them
        bottomSocketWidth += 2 * BOTTOM_SOCKET_SIDE_SPACER;

        if (bottomSocketWidth > width) {
            return (bottomSocketWidth + rb.accomodateLabelsWidth());
        }

        width += bottomSocketWidth;

        //make sure its even
        if (width % 2 == 1) {
            width++;
        }

        return width;
    }

    /** Append gp2 to gp1.  If reversed == true, then add the segments in reverse order
     * NOTE: copied and pasted from starlogoblocks/blockengine/BlockShape.java */
    private void appendPath(GeneralPath gp1, GeneralPath gp2, boolean reversed) {
        ArrayList<Number[]> points = new ArrayList<Number[]>(); // Each element is an array consisting of one Integer and six Floats

        PathIterator i = gp2.getPathIterator(new AffineTransform());

        float[] segment = new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

        float leftmost = Float.POSITIVE_INFINITY;

        while (!i.isDone()) {
            int type = i.currentSegment(segment);
            i.next();

            points.add(new Number[]{
                        new Integer(type),
                        new Float(segment[0]),
                        new Float(segment[1]),
                        new Float(segment[2]),
                        new Float(segment[3]),
                        new Float(segment[4]),
                        new Float(segment[5])
                    });
        }

        if (!reversed) {
            float deltaX = (float) gp1.getCurrentPoint().getX() - ((Float) points.get(0)[1]).floatValue();
            float deltaY = (float) gp1.getCurrentPoint().getY() - ((Float) points.get(0)[2]).floatValue();

            for (int j = 1; j < points.size(); j++) {
                Object[] typeAndPoints = points.get(j);

                int type = ((Integer) typeAndPoints[0]).intValue();
                float x1 = ((Float) typeAndPoints[1]).floatValue();
                float y1 = ((Float) typeAndPoints[2]).floatValue();
                float x2 = ((Float) typeAndPoints[3]).floatValue();
                float y2 = ((Float) typeAndPoints[4]).floatValue();
                float x3 = ((Float) typeAndPoints[5]).floatValue();
                float y3 = ((Float) typeAndPoints[6]).floatValue();

                if (type == PathIterator.SEG_MOVETO) {
                } else if (type == PathIterator.SEG_LINETO) {
                    gp1.lineTo(x1 + deltaX, y1 + deltaY);

                    leftmost = Math.min(leftmost, x1 + deltaX);
                } else if (type == PathIterator.SEG_QUADTO) {
                    gp1.quadTo(x1 + deltaX, y1 + deltaY, x2 + deltaX, y2 + deltaY);

                    leftmost = Math.min(leftmost, x2 + deltaX);
                } else if (type == PathIterator.SEG_CUBICTO) {
                    gp1.curveTo(x1 + deltaX, y1 + deltaY, x2 + deltaX, y2 + deltaY, x3 + deltaX, y3 + deltaY);

                    leftmost = Math.min(leftmost, x3 + deltaX);
                } else {
                    assert false : type;
                }
            }
        }
    }
}
TOP

Related Classes of edu.mit.blocks.codeblocks.InfixBlockShape

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.