int ClintStmtOccurrence::maximumValue(int dimIdx) const { if (dimIdx >= dimensionality() || dimIdx < 0) return 0; if (m_cachedDimMaxs.count(dimIdx) == 0) { projectOn(dimIdx, -2); // FIXME: -2 is VizProperties::NoDimension } CLINT_ASSERT(m_cachedDimMaxs.count(dimIdx) == 1, "max cache failure"); return m_cachedDimMaxs[dimIdx]; }
void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier) { gfx::Transformation::Corners oldCorners; m_currentData.transformBox(oldCorners); ContextWriter writer(m_reader, 1000); gfx::RectF bounds = m_initialData.bounds(); bool updateBounds = false; double dx, dy; dx = ((pos.x - m_catchPos.x) * cos(m_currentData.angle()) + (pos.y - m_catchPos.y) * -sin(m_currentData.angle())); dy = ((pos.x - m_catchPos.x) * sin(m_currentData.angle()) + (pos.y - m_catchPos.y) * cos(m_currentData.angle())); switch (m_handle) { case MoveHandle: if ((moveModifier & LockAxisMovement) == LockAxisMovement) { if (ABS(dx) < ABS(dy)) dx = 0.0; else dy = 0.0; } bounds.offset(dx, dy); updateBounds = true; if ((moveModifier & SnapToGridMovement) == SnapToGridMovement) { // Snap the x1,y1 point to the grid. gfx::Rect gridBounds = App::instance() ->preferences().document(m_document).grid.bounds(); gfx::PointF gridOffset( snap_to_grid( gridBounds, gfx::Point(bounds.origin()), PreferSnapTo::ClosestGridVertex)); // Now we calculate the difference from x1,y1 point and we can // use it to adjust all coordinates (x1, y1, x2, y2). gridOffset -= bounds.origin(); bounds.offset(gridOffset); } break; case ScaleNWHandle: case ScaleNHandle: case ScaleNEHandle: case ScaleWHandle: case ScaleEHandle: case ScaleSWHandle: case ScaleSHandle: case ScaleSEHandle: { static double handles[][2] = { { 0.0, 0.0 }, { 0.5, 0.0 }, { 1.0, 0.0 }, { 0.0, 0.5 }, { 1.0, 0.5 }, { 0.0, 1.0 }, { 0.5, 1.0 }, { 1.0, 1.0 } }; gfx::PointF pivot; gfx::PointF handle( handles[m_handle-ScaleNWHandle][0], handles[m_handle-ScaleNWHandle][1]); if ((moveModifier & ScaleFromPivot) == ScaleFromPivot) { pivot.x = m_currentData.pivot().x; pivot.y = m_currentData.pivot().y; } else { pivot.x = 1.0 - handle.x; pivot.y = 1.0 - handle.y; pivot.x = bounds.x + bounds.w*pivot.x; pivot.y = bounds.y + bounds.h*pivot.y; } gfx::PointF a = bounds.origin(); gfx::PointF b = bounds.point2(); if ((moveModifier & MaintainAspectRatioMovement) == MaintainAspectRatioMovement) { auto u = point2Vector(gfx::PointF(m_catchPos) - pivot); auto v = point2Vector(gfx::PointF(pos) - pivot); auto w = v.projectOn(u); double scale = u.magnitude(); if (scale != 0.0) scale = (std::fabs(w.angle()-u.angle()) < PI/2.0 ? 1.0: -1.0) * w.magnitude() / scale; else scale = 1.0; a.x = int((a.x-pivot.x)*scale + pivot.x); a.y = int((a.y-pivot.y)*scale + pivot.y); b.x = int((b.x-pivot.x)*scale + pivot.x); b.y = int((b.y-pivot.y)*scale + pivot.y); } else { handle.x = bounds.x + bounds.w*handle.x; handle.y = bounds.y + bounds.h*handle.y; double w = (handle.x-pivot.x); double h = (handle.y-pivot.y); if (m_handle == ScaleNHandle || m_handle == ScaleSHandle) { dx = 0.0; w = 1.0; // Any value != 0.0 to avoid div by zero } else if (m_handle == ScaleWHandle || m_handle == ScaleEHandle) { dy = 0.0; h = 1.0; } a.x = int((a.x-pivot.x)*(1.0+dx/w) + pivot.x); a.y = int((a.y-pivot.y)*(1.0+dy/h) + pivot.y); b.x = int((b.x-pivot.x)*(1.0+dx/w) + pivot.x); b.y = int((b.y-pivot.y)*(1.0+dy/h) + pivot.y); } // Do not use "gfx::Rect(a, b)" here because if a > b we want to // keep a rectangle with negative width or height (to know that // it was flipped). bounds.x = a.x; bounds.y = a.y; bounds.w = b.x - a.x; bounds.h = b.y - a.y; updateBounds = true; break; } case RotateNWHandle: case RotateNHandle: case RotateNEHandle: case RotateWHandle: case RotateEHandle: case RotateSWHandle: case RotateSHandle: case RotateSEHandle: { gfx::PointF abs_initial_pivot = m_initialData.pivot(); gfx::PointF abs_pivot = m_currentData.pivot(); double newAngle = m_initialData.angle() + atan2((double)(-pos.y + abs_pivot.y), (double)(+pos.x - abs_pivot.x)) - atan2((double)(-m_catchPos.y + abs_initial_pivot.y), (double)(+m_catchPos.x - abs_initial_pivot.x)); // Put the angle in -180 to 180 range. while (newAngle < -PI) newAngle += 2*PI; while (newAngle > PI) newAngle -= 2*PI; // Is the "angle snap" is activated, we've to snap the angle // to common (pixel art) angles. if ((moveModifier & AngleSnapMovement) == AngleSnapMovement) { // TODO make this configurable static const double keyAngles[] = { 0.0, 26.565, 45.0, 63.435, 90.0, 116.565, 135.0, 153.435, 180.0, 180.0, -153.435, -135.0, -116, -90.0, -63.435, -45.0, -26.565 }; double newAngleDegrees = 180.0 * newAngle / PI; int closest = 0; int last = sizeof(keyAngles) / sizeof(keyAngles[0]) - 1; for (int i=0; i<=last; ++i) { if (std::fabs(newAngleDegrees-keyAngles[closest]) > std::fabs(newAngleDegrees-keyAngles[i])) closest = i; } newAngle = PI * keyAngles[closest] / 180.0; } m_currentData.angle(newAngle); } break; case PivotHandle: { // Calculate the new position of the pivot gfx::PointF newPivot(m_initialData.pivot().x + (pos.x - m_catchPos.x), m_initialData.pivot().y + (pos.y - m_catchPos.y)); m_currentData = m_initialData; m_currentData.displacePivotTo(newPivot); } break; } if (updateBounds) { m_currentData.bounds(bounds); m_adjustPivot = true; } redrawExtraImage(); m_document->setTransformation(m_currentData); // Get the new transformed corners gfx::Transformation::Corners newCorners; m_currentData.transformBox(newCorners); // Create a union of all corners, and that will be the bounds to // redraw of the sprite. gfx::Rect fullBounds; for (int i=0; i<gfx::Transformation::Corners::NUM_OF_CORNERS; ++i) { fullBounds = fullBounds.createUnion(gfx::Rect((int)oldCorners[i].x, (int)oldCorners[i].y, 1, 1)); fullBounds = fullBounds.createUnion(gfx::Rect((int)newCorners[i].x, (int)newCorners[i].y, 1, 1)); } // If "fullBounds" is empty is because the cel was not moved if (!fullBounds.isEmpty()) { // Notify the modified region. m_document->notifySpritePixelsModified(m_sprite, gfx::Region(fullBounds), m_site.frame()); } }