Search in sources :

Example 11 with Point2f

use of com.xenoage.utils.math.geom.Point2f in project Zong by Xenoage.

the class BezierCurveTools method correctBezier.

/**
 * Corrects the given bezier curve, if it is too plain,
 * so it looks more like a part of a circle (which looks good for
 * a slur).
 */
public static CubicBezierCurve correctBezier(CubicBezierCurve curve, VSide side) {
    // compute left angle
    Point2f p1c1 = curve.getC1().sub(curve.getP1());
    Point2f p1p2 = curve.getP2().sub(curve.getP1());
    float angleLeft = (float) Math.acos(p1c1.dotProduct(p1p2) / (p1c1.length() * p1p2.length()));
    if (angleLeft < 0.3 || Float.isNaN(angleLeft)) {
        // correct curve
        float slurDown = -1 * side.getDir() * 1;
        Point2f p1 = curve.getP1().add(0, slurDown);
        Point2f c1 = curve.getC1().add(0, slurDown).rotate(p1, 1 * side.getDir() * 0.5f);
        Point2f p2 = curve.getP2().add(0, slurDown);
        Point2f c2 = curve.getC2().add(0, slurDown).rotate(p2, -1 * side.getDir() * 0.5f);
        return new CubicBezierCurve(p1, c1, c2, p2);
    } else {
        // curve is ok
        return curve;
    }
}
Also used : Point2f(com.xenoage.utils.math.geom.Point2f)

Example 12 with Point2f

use of com.xenoage.utils.math.geom.Point2f in project Zong by Xenoage.

the class QuadraticCurvesTools method computeOverConvexHull.

/**
 * Computes a list of possible quadratic curves over the given {@link ConvexHull},
 * that start at the first point of the hull and end at the last point of the hull,
 * but may also start and end further away according to the given parameters. The curve
 * will not cross the area of the convex hull.
 * @param convexHull  the convex hull
 * @param leftArea    the tolerance of the distance of the start point
 *                    (between 0 and this value). always positive or 0.
 * @param rightArea   the tolerance of the distance of the end point
 *                    (between 0 and this value). always positive or 0.
 * @return  a list of possible quadratic curves
 */
public static List<QuadraticCurve> computeOverConvexHull(ConvexHull convexHull, float leftArea, float rightArea) {
    LinkedList<QuadraticCurve> ret = new LinkedList<>();
    ArrayList<Point2f> points = convexHull.getPoints();
    VSide side = convexHull.getSide();
    int sideDir = side.getDir();
    int n = points.size();
    // compute the possible start and endpoints
    Point2f[] startPoints = new Point2f[] { points.get(0), points.get(0).add(0, sideDir * leftArea) };
    Point2f[] endPoints = new Point2f[] { points.get(n - 1), points.get(n - 1).add(0, sideDir * rightArea) };
    // the quadratic expression {a, b, c} for ax² + bx + c = 0
    // equations:
    // - (2): must start at startPoints[0] or startPoints[1] (between is never optimal!)
    // - (2): must end at endPoints[0] or endPoints[1] (between is never optimal!)
    // inequations:
    // - (0): a must be <=/>= 0 (parabola is open on the bottom/top side, dependent
    // on the side of the convex hull) - not used in SLE, checked later
    // - (m): curve must be above/below each of the m = n-2 middle points (dependent on the side)
    int m = n - 2;
    Point2f[] eq = new Point2f[2 + 2 + m];
    eq[0] = startPoints[0];
    eq[1] = startPoints[1];
    eq[2] = endPoints[0];
    eq[3] = endPoints[1];
    for (int i = 0; i < m; i++) {
        eq[4 + i] = points.get(1 + i);
    }
    // strategy, based on the simplex algorithm for linear optimization:
    // for each possible combination of 3 equations (optimum is always at the corner of the simplex,
    // so we can use the inequations like equations), solve the SLE, test, if the curve is
    // valid for all inequations, and if so, compute the area between the curve and the convex hull.
    // take the curve which has the smallest area.
    // there are ((m+4) choose 3) possible SLEs, but we have to ignore those where eq[0] AND eq[1]
    // are used and those where eq[2] AND eq[3] are used.
    int[][] subsets = getAllCombinationsOf3(m + 4);
    for (int[] eqIndices : subsets) {
        // not useable: {0,1,?}, {2,3,?} and {?,2,3}
        if (eqIndices[0] == 0 && eqIndices[1] == 1 || eqIndices[0] == 2 && eqIndices[1] == 3 || eqIndices[1] == 2 && eqIndices[2] == 3) {
        // ignore
        } else {
            // usable. solve SLE
            double[][] A = new double[3][3];
            double[] b = new double[3];
            for (int iy = 0; iy < 3; iy++) {
                Point2f p = eq[eqIndices[iy]];
                A[iy][0] = p.x * p.x;
                A[iy][1] = p.x;
                A[iy][2] = 1;
                b[iy] = p.y;
            }
            double[] params = Gauss.solve(A, b);
            // parameters ok for all equations?
            boolean ok = true;
            ok &= sideDir * getY(startPoints[0].x, params) >= sideDir * startPoints[0].y;
            ok &= sideDir * getY(startPoints[1].x, params) <= sideDir * startPoints[1].y;
            ok &= sideDir * getY(endPoints[0].x, params) >= sideDir * endPoints[0].y;
            ok &= sideDir * getY(endPoints[1].x, params) <= sideDir * endPoints[1].y;
            // parabole is open on the bottom/top side
            ok &= sideDir * params[0] <= 0;
            for (int im = 0; ok && im < m; im++) {
                ok &= sideDir * getY(points.get(1 + im).x, params) >= sideDir * points.get(1 + im).y;
            }
            if (ok) {
                // remember this equation
                ret.add(new QuadraticCurve((float) params[0], (float) params[1], (float) params[2]));
            }
        }
    }
    if (ret.size() == 0) {
        // no curve found. use direct line between first and last point.
        double[][] A = new double[][] { { points.get(0).x, 1 }, { points.get(n - 1).x, 1 } };
        double[] b = new double[] { points.get(0).y, points.get(n - 1).y };
        double[] params = Gauss.solve(A, b);
        ret.add(new QuadraticCurve(0f, (float) params[0], (float) params[1]));
    }
    // return result
    return ret;
}
Also used : LinkedList(java.util.LinkedList) VSide(com.xenoage.utils.math.VSide) QuadraticCurve(com.xenoage.utils.math.QuadraticCurve) Point2f(com.xenoage.utils.math.geom.Point2f)

Example 13 with Point2f

use of com.xenoage.utils.math.geom.Point2f in project Zong by Xenoage.

the class StaffStamper method createStaffStampings.

public StaffStampings createStaffStampings(Score score, FrameSpacing frame) {
    int systemsCount = frame.getSystems().size();
    int stavesCount = score.getStavesCount();
    List<StaffStamping> allStaves = alist(systemsCount * stavesCount);
    // go through the systems
    for (int iSystem : range(systemsCount)) {
        SystemSpacing system = frame.getSystems().get(iSystem);
        float systemXOffset = system.getMarginLeftMm();
        // create staves of the system
        float yOffset = system.getOffsetYMm();
        for (int iStaff : range(stavesCount)) {
            yOffset += system.getStaffDistanceMm(iStaff);
            int linesCount = score.getStaff(iStaff).getLinesCount();
            float interlineSpace = score.getInterlineSpace(iStaff);
            StaffStamping staff = new StaffStamping(system, iStaff, new Point2f(systemXOffset, yOffset), system.widthMm, linesCount, interlineSpace);
            allStaves.add(staff);
            yOffset += system.getStaffHeightMm(iStaff);
        }
    }
    return new StaffStampings(allStaves, systemsCount, stavesCount);
}
Also used : Point2f(com.xenoage.utils.math.geom.Point2f) StaffStamping(com.xenoage.zong.musiclayout.stampings.StaffStamping) SystemSpacing(com.xenoage.zong.musiclayout.spacing.SystemSpacing) StaffStampings(com.xenoage.zong.musiclayout.layouter.scoreframelayout.util.StaffStampings)

Example 14 with Point2f

use of com.xenoage.utils.math.geom.Point2f in project Zong by Xenoage.

the class TupletRenderer method draw.

/**
 * Draws the given {@link TupletStamping} on the given {@link Canvas},
 * using the given {@link RendererArgs}.
 */
@Override
public void draw(Stamping stamping, Canvas canvas, RendererArgs args) {
    TupletStamping tuplet = (TupletStamping) stamping;
    StaffStamping parentStaff = tuplet.parentStaff;
    float scaling = args.targetScaling;
    // horizontal position
    float x1Mm = tuplet.leftSP.xMm;
    float x2Mm = tuplet.rightSP.xMm;
    // height of hook is 1 IS
    float hookHeightPx = Units.mmToPx(parentStaff.is, scaling);
    // width and color of the line
    Color color = Color.Companion.getBlack();
    // a little bit thicker than staff line
    float width = parentStaff.getLineWidthMm() * 1.5f;
    float paintWidth;
    // compute the horizontal line and color
    float y1Mm, y2Mm;
    Color paintColor;
    if (canvas.getFormat() == CanvasFormat.Raster) {
        BitmapStaff ss = parentStaff.getBitmapInfo().getBitmapStaff(scaling);
        y1Mm = parentStaff.positionMm.y + ss.getYMm(tuplet.leftSP.lp);
        y2Mm = parentStaff.positionMm.y + ss.getYMm(tuplet.rightSP.lp);
        BitmapLine screenLine = parentStaff.getBitmapInfo().getBitmapLine(scaling, width, color);
        paintColor = screenLine.color;
        paintWidth = screenLine.widthMm;
    } else {
        y1Mm = parentStaff.computeYMm(tuplet.leftSP.lp);
        y2Mm = parentStaff.computeYMm(tuplet.rightSP.lp);
        paintColor = color;
        paintWidth = width;
    }
    // compute gap for text
    FormattedText text = tuplet.text;
    float gapMm = 0;
    float textMm = 0;
    if (text != null && text.getParagraphs().size() > 0) {
        textMm = text.getFirstParagraph().getMetrics().getWidth();
        gapMm = textMm * 2;
    }
    // draw line and hooks
    if (gapMm > 0) {
        // two lines, when there is text in between
        float xGapLMm = (x2Mm + x1Mm) / 2 - gapMm / 2;
        float xGapRMm = xGapLMm + gapMm;
        float gapVerticalMm = gapMm / (x2Mm - x1Mm) * (y2Mm - y1Mm);
        float yGapLMm = (y2Mm + y1Mm) / 2 - gapVerticalMm / 2;
        float yGapRMm = yGapLMm + gapVerticalMm;
        canvas.drawLine(new Point2f(x1Mm, y1Mm), new Point2f(xGapLMm, yGapLMm), paintColor, paintWidth);
        canvas.drawLine(new Point2f(xGapRMm, yGapRMm), new Point2f(x2Mm, y2Mm), paintColor, paintWidth);
    } else {
        // no gap
        canvas.drawLine(new Point2f(x1Mm, y1Mm), new Point2f(x2Mm, y2Mm), paintColor, paintWidth);
    }
    // hooks
    canvas.drawLine(new Point2f(x1Mm, y1Mm), new Point2f(x1Mm, y1Mm + hookHeightPx * (tuplet.leftSP.lp < 0 ? -1 : 1)), paintColor, paintWidth);
    canvas.drawLine(new Point2f(x2Mm, y2Mm), new Point2f(x2Mm, y2Mm + hookHeightPx * (tuplet.rightSP.lp < 0 ? -1 : 1)), paintColor, paintWidth);
    // draw text
    if (text != null && text.getParagraphs().size() > 0) {
        float textAscent = text.getFirstParagraph().getMetrics().getAscent();
        float textX = (x1Mm + x2Mm) / 2 - textMm / 2;
        float textY = (y1Mm + y2Mm) / 2 + textAscent / 2;
        canvas.drawText(tuplet.text, null, new Point2f(textX, textY), true, 0);
    }
}
Also used : Point2f(com.xenoage.utils.math.geom.Point2f) StaffStamping(com.xenoage.zong.musiclayout.stampings.StaffStamping) Color(com.xenoage.utils.color.Color) BitmapLine(com.xenoage.zong.musiclayout.stampings.bitmap.BitmapLine) BitmapStaff(com.xenoage.zong.musiclayout.stampings.bitmap.BitmapStaff) FormattedText(com.xenoage.zong.core.text.FormattedText) TupletStamping(com.xenoage.zong.musiclayout.stampings.TupletStamping)

Example 15 with Point2f

use of com.xenoage.utils.math.geom.Point2f in project Zong by Xenoage.

the class VoltaRenderer method draw.

/**
 * Draws the given {@link VoltaStamping} on the given {@link Canvas},
 * using the given {@link RendererArgs}.
 */
@Override
public void draw(Stamping stamping, Canvas canvas, RendererArgs args) {
    VoltaStamping volta = (VoltaStamping) stamping;
    StaffStamping parentStaff = volta.parentStaff;
    float scaling = args.scaling;
    // horizontal position
    float x1 = volta.leftXMm + parentStaff.positionMm.x;
    float x2 = volta.rightXMm + parentStaff.positionMm.x;
    // compute hooks
    boolean hook = volta.leftHook || volta.rightHook;
    float hookHeight = 0;
    if (hook) {
        // height of hook is 2 interline spaces
        hookHeight = parentStaff.is * 2;
    }
    // width and color of the line
    Color color = Color.Companion.getBlack();
    // a little bit thicker than staff line
    float width = parentStaff.getLineWidthMm() * 1.5f;
    float paintWidth;
    // compute the horizontal line and color
    float y;
    Color paintColor;
    if (canvas.getFormat() == CanvasFormat.Raster) {
        BitmapStaff ss = parentStaff.getBitmapInfo().getBitmapStaff(scaling);
        y = parentStaff.positionMm.y + ss.getYMm(volta.lp);
        BitmapLine screenLine = parentStaff.getBitmapInfo().getBitmapLine(scaling, width, color);
        paintColor = screenLine.color;
        paintWidth = screenLine.widthMm;
    } else {
        y = parentStaff.computeYMm(volta.lp);
        paintColor = color;
        paintWidth = width;
    }
    // draw line and hooks
    canvas.drawLine(new Point2f(x1, y), new Point2f(x2, y), paintColor, paintWidth);
    if (volta.leftHook) {
        canvas.drawLine(new Point2f(x1, y), new Point2f(x1, y + hookHeight), paintColor, paintWidth);
    }
    if (volta.rightHook) {
        canvas.drawLine(new Point2f(x2, y), new Point2f(x2, y + hookHeight), paintColor, paintWidth);
    }
    // draw text
    FormattedText text = volta.text;
    if (text != null && text.getParagraphs().size() > 0) {
        float textAscent = text.getFirstParagraph().getMetrics().getAscent();
        float textX = x1 + parentStaff.is * 1;
        float textY = y + textAscent;
        canvas.drawText(volta.text, null, new Point2f(textX, textY), true, 0);
    }
}
Also used : Point2f(com.xenoage.utils.math.geom.Point2f) VoltaStamping(com.xenoage.zong.musiclayout.stampings.VoltaStamping) StaffStamping(com.xenoage.zong.musiclayout.stampings.StaffStamping) Color(com.xenoage.utils.color.Color) BitmapLine(com.xenoage.zong.musiclayout.stampings.bitmap.BitmapLine) BitmapStaff(com.xenoage.zong.musiclayout.stampings.bitmap.BitmapStaff) FormattedText(com.xenoage.zong.core.text.FormattedText)

Aggregations

Point2f (com.xenoage.utils.math.geom.Point2f)51 BitmapStaff (com.xenoage.zong.musiclayout.stampings.bitmap.BitmapStaff)11 StaffStamping (com.xenoage.zong.musiclayout.stampings.StaffStamping)10 Test (org.junit.Test)10 Color (com.xenoage.utils.color.Color)7 Page (com.xenoage.zong.layout.Page)6 Size2f (com.xenoage.utils.math.geom.Size2f)5 Layout (com.xenoage.zong.layout.Layout)5 ScoreFrame (com.xenoage.zong.layout.frames.ScoreFrame)5 BitmapLine (com.xenoage.zong.musiclayout.stampings.bitmap.BitmapLine)5 Rectangle2f (com.xenoage.utils.math.geom.Rectangle2f)4 FormattedText (com.xenoage.zong.core.text.FormattedText)4 TextMetrics (com.xenoage.utils.font.TextMetrics)3 FormattedTextParagraph (com.xenoage.zong.core.text.FormattedTextParagraph)3 ScoreFrameChain (com.xenoage.zong.layout.frames.ScoreFrameChain)3 ScoreLayout (com.xenoage.zong.musiclayout.ScoreLayout)3 Paint (android.graphics.Paint)2 Point2i (com.xenoage.utils.math.geom.Point2i)2 Size2i (com.xenoage.utils.math.geom.Size2i)2 Score (com.xenoage.zong.core.Score)2