<commit_before>#include "kwm.h"

extern uint32_t MaxDisplayCount;
extern uint32_t ActiveDisplaysCount;
extern CGDirectDisplayID ActiveDisplays[];

extern std::vector<screen_info> DisplayLst;
extern std::vector<window_info> WindowLst;
extern AXUIElementRef FocusedWindowRef;
extern window_info *FocusedWindow;
extern screen_info *Screen;
extern int CurrentSpace;

void GetActiveDisplays()
{
    CGGetActiveDisplayList(MaxDisplayCount, (CGDirectDisplayID*)&ActiveDisplays, &ActiveDisplaysCount);
    for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
    {
        CGRect DisplayRect = CGDisplayBounds(ActiveDisplays[DisplayIndex]);
        screen_info Screen;
        Screen.ID = DisplayIndex;
        Screen.OldWindowListCount = -1;

        Screen.X = DisplayRect.origin.x;
        Screen.Y = DisplayRect.origin.y;
        Screen.Width = DisplayRect.size.width;
        Screen.Height = DisplayRect.size.height;

        // Read from config at some point
        Screen.PaddingTop = 40;
        Screen.PaddingLeft = 20;
        Screen.PaddingRight = 20;
        Screen.PaddingBottom = 40;

        Screen.VerticalGap = 10;
        Screen.HorizontalGap = 10;

        DisplayLst.push_back(Screen);
    }

    Screen = GetDisplayOfMousePointer();
}

screen_info *GetDisplayOfMousePointer()
{
    CGEventRef Event = CGEventCreate(NULL);
    CGPoint Cursor = CGEventGetLocation(Event);
    CFRelease(Event);

    for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
    {
        screen_info *Screen = &DisplayLst[DisplayIndex];
        if(Cursor.x >= Screen->X && Cursor.x <= Screen->X + Screen->Width &&
           Cursor.y >= Screen->Y && Cursor.y <= Screen->Y + Screen->Height)
               return Screen;
    }

    return NULL;
}

screen_info *GetDisplayOfWindow(window_info *Window)
{
    if(Window)
    {
        for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
        {
            screen_info *Screen = &DisplayLst[DisplayIndex];
            if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
                return Screen;
        }
    }

    return NULL;
}

std::vector<window_info*> GetAllWindowsOnDisplay(int ScreenIndex)
{
    screen_info *Screen = &DisplayLst[ScreenIndex];
    std::vector<window_info*> ScreenWindowLst;
    for(int WindowIndex = 0; WindowIndex < WindowLst.size(); ++WindowIndex)
    {
        window_info *Window = &WindowLst[WindowIndex];
        if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
            ScreenWindowLst.push_back(Window);
    }

    return ScreenWindowLst;
}

std::vector<int> GetAllWindowIDsOnDisplay(int ScreenIndex)
{
    screen_info *Screen = &DisplayLst[ScreenIndex];
    std::vector<int> ScreenWindowIDLst;
    for(int WindowIndex = 0; WindowIndex < WindowLst.size(); ++WindowIndex)
    {
        window_info *Window = &WindowLst[WindowIndex];
        if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
            ScreenWindowIDLst.push_back(Window->WID);
    }

    return ScreenWindowIDLst;
}

void ChangePaddingOfDisplay(const std::string &Side, int Offset)
{
    screen_info *Screen = GetDisplayOfMousePointer();
    if(Side == "Left")
        Screen->PaddingLeft += Offset;
    else if(Side == "Right")
        Screen->PaddingRight += Offset;
    else if(Side == "Top")
        Screen->PaddingTop += Offset;
    else if(Side == "Bottom")
        Screen->PaddingBottom += Offset;

    SetRootNodeContainer(Screen, Screen->Space[CurrentSpace]);
    CreateNodeContainers(Screen, Screen->Space[CurrentSpace]);
    ApplyNodeContainer(Screen->Space[CurrentSpace]);
}

void ChangeGapOfDisplay(const std::string &Side, int Offset)
{
    screen_info *Screen = GetDisplayOfMousePointer();
    if(Side == "Vertical")
        Screen->VerticalGap += Offset;
    else if(Side == "Horizontal")
        Screen->HorizontalGap += Offset;

    CreateNodeContainers(Screen, Screen->Space[CurrentSpace]);
    ApplyNodeContainer(Screen->Space[CurrentSpace]);
}

void CycleFocusedWindowDisplay(int Shift)
{
    screen_info *Screen = GetDisplayOfWindow(FocusedWindow);
    int NewScreenIndex;

    if(Shift == 1)
        NewScreenIndex = (Screen->ID + 1 >= ActiveDisplaysCount) ? 0 : Screen->ID + 1;
    else if(Shift == -1)
        NewScreenIndex = (Screen->ID - 1 < 0) ? ActiveDisplaysCount - 1 : Screen->ID - 1;
    else
        return;

    screen_info *NewScreen = &DisplayLst[NewScreenIndex];
    SetWindowDimensions(FocusedWindowRef,
            FocusedWindow,
            NewScreen->X + 30,
            NewScreen->Y + 40,
            FocusedWindow->Width,
            FocusedWindow->Height);
}
<commit_msg>should no longer incorrectly move window when no external monitors have been connected<commit_after>#include "kwm.h"

extern uint32_t MaxDisplayCount;
extern uint32_t ActiveDisplaysCount;
extern CGDirectDisplayID ActiveDisplays[];

extern std::vector<screen_info> DisplayLst;
extern std::vector<window_info> WindowLst;
extern AXUIElementRef FocusedWindowRef;
extern window_info *FocusedWindow;
extern screen_info *Screen;
extern int CurrentSpace;

void GetActiveDisplays()
{
    CGGetActiveDisplayList(MaxDisplayCount, (CGDirectDisplayID*)&ActiveDisplays, &ActiveDisplaysCount);
    for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
    {
        CGRect DisplayRect = CGDisplayBounds(ActiveDisplays[DisplayIndex]);
        screen_info Screen;
        Screen.ID = DisplayIndex;
        Screen.OldWindowListCount = -1;

        Screen.X = DisplayRect.origin.x;
        Screen.Y = DisplayRect.origin.y;
        Screen.Width = DisplayRect.size.width;
        Screen.Height = DisplayRect.size.height;

        // Read from config at some point
        Screen.PaddingTop = 40;
        Screen.PaddingLeft = 20;
        Screen.PaddingRight = 20;
        Screen.PaddingBottom = 40;

        Screen.VerticalGap = 10;
        Screen.HorizontalGap = 10;

        DisplayLst.push_back(Screen);
    }

    Screen = GetDisplayOfMousePointer();
}

screen_info *GetDisplayOfMousePointer()
{
    CGEventRef Event = CGEventCreate(NULL);
    CGPoint Cursor = CGEventGetLocation(Event);
    CFRelease(Event);

    for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
    {
        screen_info *Screen = &DisplayLst[DisplayIndex];
        if(Cursor.x >= Screen->X && Cursor.x <= Screen->X + Screen->Width &&
           Cursor.y >= Screen->Y && Cursor.y <= Screen->Y + Screen->Height)
               return Screen;
    }

    return NULL;
}

screen_info *GetDisplayOfWindow(window_info *Window)
{
    if(Window)
    {
        for(int DisplayIndex = 0; DisplayIndex < ActiveDisplaysCount; ++DisplayIndex)
        {
            screen_info *Screen = &DisplayLst[DisplayIndex];
            if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
                return Screen;
        }
    }

    return NULL;
}

std::vector<window_info*> GetAllWindowsOnDisplay(int ScreenIndex)
{
    screen_info *Screen = &DisplayLst[ScreenIndex];
    std::vector<window_info*> ScreenWindowLst;
    for(int WindowIndex = 0; WindowIndex < WindowLst.size(); ++WindowIndex)
    {
        window_info *Window = &WindowLst[WindowIndex];
        if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
            ScreenWindowLst.push_back(Window);
    }

    return ScreenWindowLst;
}

std::vector<int> GetAllWindowIDsOnDisplay(int ScreenIndex)
{
    screen_info *Screen = &DisplayLst[ScreenIndex];
    std::vector<int> ScreenWindowIDLst;
    for(int WindowIndex = 0; WindowIndex < WindowLst.size(); ++WindowIndex)
    {
        window_info *Window = &WindowLst[WindowIndex];
        if(Window->X >= Screen->X && Window->X <= Screen->X + Screen->Width)
            ScreenWindowIDLst.push_back(Window->WID);
    }

    return ScreenWindowIDLst;
}

void ChangePaddingOfDisplay(const std::string &Side, int Offset)
{
    screen_info *Screen = GetDisplayOfMousePointer();
    if(Side == "Left")
        Screen->PaddingLeft += Offset;
    else if(Side == "Right")
        Screen->PaddingRight += Offset;
    else if(Side == "Top")
        Screen->PaddingTop += Offset;
    else if(Side == "Bottom")
        Screen->PaddingBottom += Offset;

    SetRootNodeContainer(Screen, Screen->Space[CurrentSpace]);
    CreateNodeContainers(Screen, Screen->Space[CurrentSpace]);
    ApplyNodeContainer(Screen->Space[CurrentSpace]);
}

void ChangeGapOfDisplay(const std::string &Side, int Offset)
{
    screen_info *Screen = GetDisplayOfMousePointer();
    if(Side == "Vertical")
        Screen->VerticalGap += Offset;
    else if(Side == "Horizontal")
        Screen->HorizontalGap += Offset;

    CreateNodeContainers(Screen, Screen->Space[CurrentSpace]);
    ApplyNodeContainer(Screen->Space[CurrentSpace]);
}

void CycleFocusedWindowDisplay(int Shift)
{
    screen_info *Screen = GetDisplayOfWindow(FocusedWindow);
    int NewScreenIndex = -1;

    if(Shift == 1)
        NewScreenIndex = (Screen->ID + 1 >= ActiveDisplaysCount) ? 0 : Screen->ID + 1;
    else if(Shift == -1)
        NewScreenIndex = (Screen->ID - 1 < 0) ? ActiveDisplaysCount - 1 : Screen->ID - 1;

    if(NewScreenIndex != Screen->ID)
    {
        screen_info *NewScreen = &DisplayLst[NewScreenIndex];
        SetWindowDimensions(FocusedWindowRef,
                FocusedWindow,
                NewScreen->X + 30,
                NewScreen->Y + 40,
                FocusedWindow->Width,
                FocusedWindow->Height);
    }
}
<|endoftext|>


<commit_before>//=====================================================================//
/*!	@file
	@brief	LED 点滅サンプル @n
			LED は、P43(2) に接続 @n
			ex: Vcc = LED(A), P43 = LED(K) 吸い込み @n
			    ※必ず直列に適等な抵抗を入れて電流を調整する。@n
				・3.3V: 赤色 LED(VF: 1.7V)、1mA、1.5K @n
				・5.0V: 赤色 LED(VF: 1.7V)、1mA、3.3K @n
				・5.0V: 赤色 LED(VF: 1.7V)、3mA、1.1K
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RL78/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdint>
#include "common/renesas.hpp"
#include "common/port_utils.hpp"

namespace {

	void wait_()
	{
		asm("nop");
	}

}

int main(int argc, char* argv[])
{
	utils::port::pullup_all();  ///< 安全の為、全ての入力をプルアップ

	device::PM4.B3 = 0;  // output

	bool f = false;
	while(1) {
		for(uint32_t i = 0; i < 100000; ++i) {
			wait_();
		}
		device::P4.B3 = f;
		f = !f;
	}
}
<commit_msg>cleanup<commit_after>//=====================================================================//
/*!	@file
	@brief	LED 点滅サンプル @n
			LED は、P43(2) に接続 @n
			ex: Vcc = LED(A), P43 = LED(K) 吸い込み @n
			    ※必ず直列に適等な抵抗を入れて電流を調整する。@n
				・3.3V: 赤色 LED(VF: 1.7V)、1mA、1.5K @n
				・5.0V: 赤色 LED(VF: 1.7V)、1mA、3.3K @n
				・5.0V: 赤色 LED(VF: 1.7V)、3mA、1.1K
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RL78/blob/master/LICENSE
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/port_utils.hpp"

namespace {

	void wait_()
	{
		asm("nop");
	}

}

int main(int argc, char* argv[])
{
	utils::port::pullup_all();  ///< 安全の為、全ての入力をプルアップ

	device::PM4.B3 = 0;  // output

	bool f = false;
	while(1) {
		for(uint32_t i = 0; i < 100000; ++i) {
			wait_();
		}
		device::P4.B3 = f;
		f = !f;
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 The Regents of the University of California (Regents).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
//     * Neither the name of The Regents or University of California nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Please contact the author of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

#include "theia/sfm/global_pose_estimation/nonlinear_position_estimator.h"

#include <ceres/ceres.h>
#include <ceres/rotation.h>
#include <Eigen/Core>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "theia/sfm/global_pose_estimation/pairwise_translation_error.h"
#include "theia/sfm/reconstruction.h"
#include "theia/sfm/types.h"
#include "theia/util/map_util.h"
#include "theia/util/util.h"

namespace theia {
namespace {

using Eigen::Matrix3d;
using Eigen::Vector3d;

Vector3d GetRotatedTranslation(const Vector3d& rotation_angle_axis,
                               const Vector3d& translation) {
  Matrix3d rotation;
  ceres::AngleAxisToRotationMatrix(
      rotation_angle_axis.data(),
      ceres::ColumnMajorAdapter3x3(rotation.data()));
  return rotation.transpose() * translation;
}

Vector3d GetRotatedFeatureRay(const Camera& camera,
                              const Vector3d& orientation,
                              const Feature& feature) {
  Camera temp_camera = camera;
  temp_camera.SetOrientationFromAngleAxis(orientation);
  // Get the image ray rotated into the world reference frame.
  return camera.PixelToUnitDepthRay(feature).normalized();
}

// Sorts the pairs such that the number of views (i.e. the int) is sorted in
// descending order.
bool CompareViewsPerTrack(const std::pair<TrackId, int>& t1,
                          const std::pair<TrackId, int>& t2) {
  return t1.second > t2.second;
}

}  // namespace

NonlinearPositionEstimator::NonlinearPositionEstimator(
    const NonlinearPositionEstimator::Options& options,
    const Reconstruction& reconstruction)
    : options_(options), reconstruction_(reconstruction) {
  CHECK_GT(options_.num_threads, 0);
  CHECK_GE(options_.min_num_points_per_view, 0);
  CHECK_GT(options_.point_to_camera_weight, 0);
  CHECK_GT(options_.robust_loss_width, 0);
}

bool NonlinearPositionEstimator::EstimatePositions(
    const std::unordered_map<ViewIdPair, TwoViewInfo>& view_pairs,
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  CHECK_NOTNULL(positions);
  triangulated_points_.clear();
  problem_.reset(new ceres::Problem());
  view_pairs_ = &view_pairs;

  // Iterative schur is only used if the problem is large enough, otherwise
  // sparse schur is used.
  static const int kMinNumCamerasForIterativeSolve = 1000;

  // Initialize positions to be random.
  InitializeRandomPositions(orientations, positions);

  // Add the constraints to the problem.
  AddCameraToCameraConstraints(orientations, positions);
  if (options_.min_num_points_per_view > 0) {
    AddPointToCameraConstraints(orientations, positions);
    AddCamerasAndPointsToParameterGroups(positions);
  }

  // Set one camera to be at the origin to remove the ambiguity of the origin.
  positions->begin()->second.setZero();
  problem_->SetParameterBlockConstant(positions->begin()->second.data());

  // Set the solver options.
  ceres::Solver::Summary summary;
  solver_options_.num_threads = options_.num_threads;
  solver_options_.num_linear_solver_threads = options_.num_threads;
  solver_options_.max_num_iterations = options_.max_num_iterations;

  // Choose the type of linear solver. For sufficiently large problems, we want
  // to use iterative methods (e.g., Conjugate Gradient or Iterative Schur);
  // however, we only want to use a Schur solver if 3D points are used in the
  // optimization.
  if (positions->size() > kMinNumCamerasForIterativeSolve) {
    if (options_.min_num_points_per_view > 0) {
      solver_options_.linear_solver_type = ceres::ITERATIVE_SCHUR;
      solver_options_.preconditioner_type = ceres::SCHUR_JACOBI;
    } else {
      solver_options_.linear_solver_type = ceres::CGNR;
      solver_options_.preconditioner_type = ceres::JACOBI;
    }
  } else {
    if (options_.min_num_points_per_view > 0) {
      solver_options_.linear_solver_type = ceres::SPARSE_SCHUR;
    } else {
      solver_options_.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
    }
  }

  ceres::Solve(solver_options_, problem_.get(), &summary);
  LOG(INFO) << summary.FullReport();
  return summary.IsSolutionUsable();
}

void NonlinearPositionEstimator::InitializeRandomPositions(
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  std::unordered_set<ViewId> constrained_positions;
  constrained_positions.reserve(orientations.size());
  for (const auto& view_pair : *view_pairs_) {
    constrained_positions.insert(view_pair.first.first);
    constrained_positions.insert(view_pair.first.second);
  }

  positions->reserve(orientations.size());
  for (const auto& orientation : orientations) {
    if (ContainsKey(constrained_positions, orientation.first)) {
      (*positions)[orientation.first] = 100.0 * Vector3d::Random();
    }
  }
}

void NonlinearPositionEstimator::AddCameraToCameraConstraints(
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  for (const auto& view_pair : *view_pairs_) {
    const ViewId view_id1 = view_pair.first.first;
    const ViewId view_id2 = view_pair.first.second;
    Vector3d* position1 = FindOrNull(*positions, view_id1);
    Vector3d* position2 = FindOrNull(*positions, view_id2);

    // Do not add this view pair if one or both of the positions do not exist.
    if (position1 == nullptr || position2 == nullptr) {
      continue;
    }

    // Rotate the relative translation so that it is aligned to the global
    // orientation frame.
    const Vector3d translation_direction = GetRotatedTranslation(
        FindOrDie(orientations, view_id1), view_pair.second.position_2);

    ceres::CostFunction* cost_function =
        PairwiseTranslationError::Create(translation_direction, 1.0);

    problem_->AddResidualBlock(cost_function,
                               new ceres::HuberLoss(options_.robust_loss_width),
                               position1->data(),
                               position2->data());
  }

  VLOG(2) << problem_->NumResidualBlocks() << " camera to camera constraints "
                                              "were added to the position "
                                              "estimation problem.";
}

void NonlinearPositionEstimator::AddPointToCameraConstraints(
    const std::unordered_map<ViewId, Eigen::Vector3d>& orientations,
    std::unordered_map<ViewId, Eigen::Vector3d>* positions) {
  const int num_camera_to_camera_constraints = problem_->NumResidualBlocks();
  std::unordered_set<TrackId> tracks_to_add;
  const int num_point_to_camera_constraints =
      FindTracksForProblem(*positions, &tracks_to_add);
  if (num_point_to_camera_constraints == 0) {
    return;
  }

  const double point_to_camera_weight =
      options_.point_to_camera_weight *
      static_cast<double>(num_camera_to_camera_constraints) /
      static_cast<double>(num_point_to_camera_constraints);

  triangulated_points_.reserve(tracks_to_add.size());
  for (const TrackId track_id : tracks_to_add) {
    triangulated_points_[track_id] = 100.0 * Vector3d::Random();

    AddTrackToProblem(track_id,
                      orientations,
                      point_to_camera_weight,
                      positions);
  }

  VLOG(2) << num_point_to_camera_constraints << " point to camera constriants "
                                                "were added to the position "
                                                "estimation problem.";
}

int NonlinearPositionEstimator::FindTracksForProblem(
    const std::unordered_map<ViewId, Eigen::Vector3d>& positions,
    std::unordered_set<TrackId>* tracks_to_add) {
  CHECK_NOTNULL(tracks_to_add)->clear();

  std::unordered_map<ViewId, int> tracks_per_camera;
  for (const auto& position : positions) {
    tracks_per_camera[position.first] = 0;
  }

  // Add the tracks that see the most views until each camera has the minimum
  // number of tracks.
  for (const auto& position : positions) {
    const View* view = reconstruction_.View(position.first);
    if (view == nullptr ||
        view->NumFeatures() < options_.min_num_points_per_view) {
      continue;
    }

    // Get the tracks in sorted order so that we add the tracks that see the
    // most cameras first.
    const std::vector<TrackId>& sorted_tracks =
        GetTracksSortedByNumViews(reconstruction_, *view, *tracks_to_add);

    for (int i = 0;
         i < sorted_tracks.size() && tracks_per_camera[position.first] <
                                         options_.min_num_points_per_view;
         i++) {
      // Update the number of point to camera constraints for each camera.
      tracks_to_add->insert(sorted_tracks[i]);
      for (const ViewId view_id :
           reconstruction_.Track(sorted_tracks[i])->ViewIds()) {
        if (!ContainsKey(positions, view_id)) {
          continue;
        }
        ++tracks_per_camera[view_id];
      }
    }
  }

  int num_point_to_camera_constraints = 0;
  for (const auto& tracks_in_camera : tracks_per_camera) {
    num_point_to_camera_constraints += tracks_in_camera.second;
  }
  return num_point_to_camera_constraints;
}

std::vector<TrackId> NonlinearPositionEstimator::GetTracksSortedByNumViews(
    const Reconstruction& reconstruction,
    const View& view,
    const std::unordered_set<TrackId>& existing_tracks) {
  std::vector<std::pair<TrackId, int> > views_per_track;
  views_per_track.reserve(view.NumFeatures());
  const auto& track_ids = view.TrackIds();
  for (const auto& track_id : track_ids) {
    const Track* track = reconstruction.Track(track_id);

    if (track == nullptr || ContainsKey(existing_tracks, track_id)) {
      continue;
    }
    views_per_track.emplace_back(track_id, track->NumViews());
  }

  // Return an empty array if no tracks could be found for this view.
  std::vector<TrackId> sorted_tracks(views_per_track.size());
  if (views_per_track.size() == 0) {
    return sorted_tracks;
  }

  // Sort the tracks by the number of views. Only sort the first few tracks
  // since those are the ones that will be added to the problem.
  const int num_tracks_to_sort =
      std::min(static_cast<int>(views_per_track.size()),
               options_.min_num_points_per_view);
  std::partial_sort(views_per_track.begin(),
                    views_per_track.begin() + num_tracks_to_sort,
                    views_per_track.end(), CompareViewsPerTrack);

  for (int i = 0; i < num_tracks_to_sort; i++) {
    sorted_tracks[i] = views_per_track[i].first;
  }
  return sorted_tracks;
}

void NonlinearPositionEstimator::AddTrackToProblem(
    const TrackId track_id,
    const std::unordered_map<ViewId, Vector3d>& orientations,
    const double point_to_camera_weight,
    std::unordered_map<ViewId, Vector3d>* positions) {
  // For each view in the track add the point to camera correspondences.
  for (const ViewId view_id : reconstruction_.Track(track_id)->ViewIds()) {
    if (!ContainsKey(*positions, view_id)) {
      continue;
    }
    Vector3d& camera_position = FindOrDie(*positions, view_id);
    Vector3d& point = FindOrDie(triangulated_points_, track_id);

    // Rotate the feature ray to be in the global orientation frame.
    const Vector3d feature_ray = GetRotatedFeatureRay(
        reconstruction_.View(view_id)->Camera(),
        FindOrDie(orientations, view_id),
        *reconstruction_.View(view_id)->GetFeature(track_id));

    // Rotate the relative translation so that it is aligned to the global
    // orientation frame.
    ceres::CostFunction* cost_function =
        PairwiseTranslationError::Create(feature_ray, point_to_camera_weight);

    // Add the residual block
    problem_->AddResidualBlock(cost_function,
                               new ceres::HuberLoss(options_.robust_loss_width),
                               camera_position.data(),
                               point.data());
  }
}

void NonlinearPositionEstimator::AddCamerasAndPointsToParameterGroups(
    std::unordered_map<ViewId, Vector3d>* positions) {
  CHECK_GT(triangulated_points_.size(), 0)
      << "Cannot set the Ceres parameter groups for Schur based solvers "
         "because there are no triangulated points.";

  // Create a custom ordering for Schur-based problems.
  solver_options_.linear_solver_ordering.reset(
      new ceres::ParameterBlockOrdering);
  ceres::ParameterBlockOrdering* parameter_ordering =
      solver_options_.linear_solver_ordering.get();
  // Add point parameters to group 0.
  for (auto& point : triangulated_points_) {
    parameter_ordering->AddElementToGroup(point.second.data(), 0);
  }

  // Add camera parameters to group 1.
  for (auto& position : *positions) {
    parameter_ordering->AddElementToGroup(position.second.data(), 1);
  }
}

}  // namespace theia
<commit_msg>Fixing bug when view_pairs is empty and trying to estimate position.<commit_after>// Copyright (C) 2015 The Regents of the University of California (Regents).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
//     * Neither the name of The Regents or University of California nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Please contact the author of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

#include "theia/sfm/global_pose_estimation/nonlinear_position_estimator.h"

#include <ceres/ceres.h>
#include <ceres/rotation.h>
#include <Eigen/Core>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "theia/sfm/global_pose_estimation/pairwise_translation_error.h"
#include "theia/sfm/reconstruction.h"
#include "theia/sfm/types.h"
#include "theia/util/map_util.h"
#include "theia/util/util.h"

namespace theia {
namespace {

using Eigen::Matrix3d;
using Eigen::Vector3d;

Vector3d GetRotatedTranslation(const Vector3d& rotation_angle_axis,
                               const Vector3d& translation) {
  Matrix3d rotation;
  ceres::AngleAxisToRotationMatrix(
      rotation_angle_axis.data(),
      ceres::ColumnMajorAdapter3x3(rotation.data()));
  return rotation.transpose() * translation;
}

Vector3d GetRotatedFeatureRay(const Camera& camera,
                              const Vector3d& orientation,
                              const Feature& feature) {
  Camera temp_camera = camera;
  temp_camera.SetOrientationFromAngleAxis(orientation);
  // Get the image ray rotated into the world reference frame.
  return camera.PixelToUnitDepthRay(feature).normalized();
}

// Sorts the pairs such that the number of views (i.e. the int) is sorted in
// descending order.
bool CompareViewsPerTrack(const std::pair<TrackId, int>& t1,
                          const std::pair<TrackId, int>& t2) {
  return t1.second > t2.second;
}

}  // namespace

NonlinearPositionEstimator::NonlinearPositionEstimator(
    const NonlinearPositionEstimator::Options& options,
    const Reconstruction& reconstruction)
    : options_(options), reconstruction_(reconstruction) {
  CHECK_GT(options_.num_threads, 0);
  CHECK_GE(options_.min_num_points_per_view, 0);
  CHECK_GT(options_.point_to_camera_weight, 0);
  CHECK_GT(options_.robust_loss_width, 0);
}

bool NonlinearPositionEstimator::EstimatePositions(
    const std::unordered_map<ViewIdPair, TwoViewInfo>& view_pairs,
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  CHECK_NOTNULL(positions);
  if (view_pairs.empty() || orientations.empty()) {
    VLOG(2) << "Number of view_pairs = " << view_pairs.size()
            << " Number of orientations = " << orientations.size();
    return false;
  }
  triangulated_points_.clear();
  problem_.reset(new ceres::Problem());
  view_pairs_ = &view_pairs;

  // Iterative schur is only used if the problem is large enough, otherwise
  // sparse schur is used.
  static const int kMinNumCamerasForIterativeSolve = 1000;

  // Initialize positions to be random.
  InitializeRandomPositions(orientations, positions);

  // Add the constraints to the problem.
  AddCameraToCameraConstraints(orientations, positions);
  if (options_.min_num_points_per_view > 0) {
    AddPointToCameraConstraints(orientations, positions);
    AddCamerasAndPointsToParameterGroups(positions);
  }

  // Set one camera to be at the origin to remove the ambiguity of the origin.
  positions->begin()->second.setZero();
  problem_->SetParameterBlockConstant(positions->begin()->second.data());

  // Set the solver options.
  ceres::Solver::Summary summary;
  solver_options_.num_threads = options_.num_threads;
  solver_options_.num_linear_solver_threads = options_.num_threads;
  solver_options_.max_num_iterations = options_.max_num_iterations;

  // Choose the type of linear solver. For sufficiently large problems, we want
  // to use iterative methods (e.g., Conjugate Gradient or Iterative Schur);
  // however, we only want to use a Schur solver if 3D points are used in the
  // optimization.
  if (positions->size() > kMinNumCamerasForIterativeSolve) {
    if (options_.min_num_points_per_view > 0) {
      solver_options_.linear_solver_type = ceres::ITERATIVE_SCHUR;
      solver_options_.preconditioner_type = ceres::SCHUR_JACOBI;
    } else {
      solver_options_.linear_solver_type = ceres::CGNR;
      solver_options_.preconditioner_type = ceres::JACOBI;
    }
  } else {
    if (options_.min_num_points_per_view > 0) {
      solver_options_.linear_solver_type = ceres::SPARSE_SCHUR;
    } else {
      solver_options_.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
    }
  }

  ceres::Solve(solver_options_, problem_.get(), &summary);
  LOG(INFO) << summary.FullReport();
  return summary.IsSolutionUsable();
}

void NonlinearPositionEstimator::InitializeRandomPositions(
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  std::unordered_set<ViewId> constrained_positions;
  constrained_positions.reserve(orientations.size());
  for (const auto& view_pair : *view_pairs_) {
    constrained_positions.insert(view_pair.first.first);
    constrained_positions.insert(view_pair.first.second);
  }

  positions->reserve(orientations.size());
  for (const auto& orientation : orientations) {
    if (ContainsKey(constrained_positions, orientation.first)) {
      (*positions)[orientation.first] = 100.0 * Vector3d::Random();
    }
  }
}

void NonlinearPositionEstimator::AddCameraToCameraConstraints(
    const std::unordered_map<ViewId, Vector3d>& orientations,
    std::unordered_map<ViewId, Vector3d>* positions) {
  for (const auto& view_pair : *view_pairs_) {
    const ViewId view_id1 = view_pair.first.first;
    const ViewId view_id2 = view_pair.first.second;
    Vector3d* position1 = FindOrNull(*positions, view_id1);
    Vector3d* position2 = FindOrNull(*positions, view_id2);

    // Do not add this view pair if one or both of the positions do not exist.
    if (position1 == nullptr || position2 == nullptr) {
      continue;
    }

    // Rotate the relative translation so that it is aligned to the global
    // orientation frame.
    const Vector3d translation_direction = GetRotatedTranslation(
        FindOrDie(orientations, view_id1), view_pair.second.position_2);

    ceres::CostFunction* cost_function =
        PairwiseTranslationError::Create(translation_direction, 1.0);

    problem_->AddResidualBlock(cost_function,
                               new ceres::HuberLoss(options_.robust_loss_width),
                               position1->data(),
                               position2->data());
  }

  VLOG(2) << problem_->NumResidualBlocks() << " camera to camera constraints "
                                              "were added to the position "
                                              "estimation problem.";
}

void NonlinearPositionEstimator::AddPointToCameraConstraints(
    const std::unordered_map<ViewId, Eigen::Vector3d>& orientations,
    std::unordered_map<ViewId, Eigen::Vector3d>* positions) {
  const int num_camera_to_camera_constraints = problem_->NumResidualBlocks();
  std::unordered_set<TrackId> tracks_to_add;
  const int num_point_to_camera_constraints =
      FindTracksForProblem(*positions, &tracks_to_add);
  if (num_point_to_camera_constraints == 0) {
    return;
  }

  const double point_to_camera_weight =
      options_.point_to_camera_weight *
      static_cast<double>(num_camera_to_camera_constraints) /
      static_cast<double>(num_point_to_camera_constraints);

  triangulated_points_.reserve(tracks_to_add.size());
  for (const TrackId track_id : tracks_to_add) {
    triangulated_points_[track_id] = 100.0 * Vector3d::Random();

    AddTrackToProblem(track_id,
                      orientations,
                      point_to_camera_weight,
                      positions);
  }

  VLOG(2) << num_point_to_camera_constraints << " point to camera constriants "
                                                "were added to the position "
                                                "estimation problem.";
}

int NonlinearPositionEstimator::FindTracksForProblem(
    const std::unordered_map<ViewId, Eigen::Vector3d>& positions,
    std::unordered_set<TrackId>* tracks_to_add) {
  CHECK_NOTNULL(tracks_to_add)->clear();

  std::unordered_map<ViewId, int> tracks_per_camera;
  for (const auto& position : positions) {
    tracks_per_camera[position.first] = 0;
  }

  // Add the tracks that see the most views until each camera has the minimum
  // number of tracks.
  for (const auto& position : positions) {
    const View* view = reconstruction_.View(position.first);
    if (view == nullptr ||
        view->NumFeatures() < options_.min_num_points_per_view) {
      continue;
    }

    // Get the tracks in sorted order so that we add the tracks that see the
    // most cameras first.
    const std::vector<TrackId>& sorted_tracks =
        GetTracksSortedByNumViews(reconstruction_, *view, *tracks_to_add);

    for (int i = 0;
         i < sorted_tracks.size() && tracks_per_camera[position.first] <
                                         options_.min_num_points_per_view;
         i++) {
      // Update the number of point to camera constraints for each camera.
      tracks_to_add->insert(sorted_tracks[i]);
      for (const ViewId view_id :
           reconstruction_.Track(sorted_tracks[i])->ViewIds()) {
        if (!ContainsKey(positions, view_id)) {
          continue;
        }
        ++tracks_per_camera[view_id];
      }
    }
  }

  int num_point_to_camera_constraints = 0;
  for (const auto& tracks_in_camera : tracks_per_camera) {
    num_point_to_camera_constraints += tracks_in_camera.second;
  }
  return num_point_to_camera_constraints;
}

std::vector<TrackId> NonlinearPositionEstimator::GetTracksSortedByNumViews(
    const Reconstruction& reconstruction,
    const View& view,
    const std::unordered_set<TrackId>& existing_tracks) {
  std::vector<std::pair<TrackId, int> > views_per_track;
  views_per_track.reserve(view.NumFeatures());
  const auto& track_ids = view.TrackIds();
  for (const auto& track_id : track_ids) {
    const Track* track = reconstruction.Track(track_id);

    if (track == nullptr || ContainsKey(existing_tracks, track_id)) {
      continue;
    }
    views_per_track.emplace_back(track_id, track->NumViews());
  }

  // Return an empty array if no tracks could be found for this view.
  std::vector<TrackId> sorted_tracks(views_per_track.size());
  if (views_per_track.size() == 0) {
    return sorted_tracks;
  }

  // Sort the tracks by the number of views. Only sort the first few tracks
  // since those are the ones that will be added to the problem.
  const int num_tracks_to_sort =
      std::min(static_cast<int>(views_per_track.size()),
               options_.min_num_points_per_view);
  std::partial_sort(views_per_track.begin(),
                    views_per_track.begin() + num_tracks_to_sort,
                    views_per_track.end(), CompareViewsPerTrack);

  for (int i = 0; i < num_tracks_to_sort; i++) {
    sorted_tracks[i] = views_per_track[i].first;
  }
  return sorted_tracks;
}

void NonlinearPositionEstimator::AddTrackToProblem(
    const TrackId track_id,
    const std::unordered_map<ViewId, Vector3d>& orientations,
    const double point_to_camera_weight,
    std::unordered_map<ViewId, Vector3d>* positions) {
  // For each view in the track add the point to camera correspondences.
  for (const ViewId view_id : reconstruction_.Track(track_id)->ViewIds()) {
    if (!ContainsKey(*positions, view_id)) {
      continue;
    }
    Vector3d& camera_position = FindOrDie(*positions, view_id);
    Vector3d& point = FindOrDie(triangulated_points_, track_id);

    // Rotate the feature ray to be in the global orientation frame.
    const Vector3d feature_ray = GetRotatedFeatureRay(
        reconstruction_.View(view_id)->Camera(),
        FindOrDie(orientations, view_id),
        *reconstruction_.View(view_id)->GetFeature(track_id));

    // Rotate the relative translation so that it is aligned to the global
    // orientation frame.
    ceres::CostFunction* cost_function =
        PairwiseTranslationError::Create(feature_ray, point_to_camera_weight);

    // Add the residual block
    problem_->AddResidualBlock(cost_function,
                               new ceres::HuberLoss(options_.robust_loss_width),
                               camera_position.data(),
                               point.data());
  }
}

void NonlinearPositionEstimator::AddCamerasAndPointsToParameterGroups(
    std::unordered_map<ViewId, Vector3d>* positions) {
  CHECK_GT(triangulated_points_.size(), 0)
      << "Cannot set the Ceres parameter groups for Schur based solvers "
         "because there are no triangulated points.";

  // Create a custom ordering for Schur-based problems.
  solver_options_.linear_solver_ordering.reset(
      new ceres::ParameterBlockOrdering);
  ceres::ParameterBlockOrdering* parameter_ordering =
      solver_options_.linear_solver_ordering.get();
  // Add point parameters to group 0.
  for (auto& point : triangulated_points_) {
    parameter_ordering->AddElementToGroup(point.second.data(), 0);
  }

  // Add camera parameters to group 1.
  for (auto& position : *positions) {
    parameter_ordering->AddElementToGroup(position.second.data(), 1);
  }
}

}  // namespace theia
<|endoftext|>


<commit_before>#include "font.hpp"
#include "lane.hpp"
#include "glx.hpp"
#include "sys_uniform.hpp"

namespace rs {
	// --------------------------- Face ---------------------------
	namespace {
		// 最低サイズ2bits, Layer1=4bits, Layer0=6bits = 12bits(4096)
		using LAlloc = LaneAlloc<6,4,2>;
	}
	// フォントのHeightとラインのHeightは違う！
	Face::Face(const SPString& name, const spn::PowSize& sfcSize, CCoreID cid, FontChMap& m):
		faceName(name), dep(*name, cid), coreID(cid), cplane(sfcSize, dep.height(), UPLaneAlloc(new LAlloc())), fontMap(m) {}
	Face::Face(Face&& f):
		faceName(std::move(f.faceName)), dep(std::move(f.dep)), coreID(std::move(f.coreID)), cplane(std::move(f.cplane)), fontMap(f.fontMap) {}
	bool Face::operator != (const std::string& name) const {
		return !(this->operator == (name));
	}
	bool Face::operator ==(const std::string& name) const {
		return *faceName == name;
	}
	bool Face::operator != (CCoreID cid) const {
		return !(this->operator == (cid));
	}
	bool Face::operator == (CCoreID cid) const {
		return coreID == cid;
	}
	const CharPos* Face::getCharPos(char32_t c) {
		CharID chID(c, coreID);
		// キャッシュが既にあればそれを使う
		auto itr = fontMap.find(chID);
		if(itr != fontMap.end())
			return &itr->second;

		// CharMapにエントリを作る
		CharPos& cp = fontMap[chID];
		// Dependクラスから文字のビットデータを取得
		auto res = dep.getChara(c);
		// この時点では1ピクセル8bitなので、32bitRGBAに展開
		res.first = Convert8Bit_Packed32Bit(&res.first[0], res.second.width(), res.second.width(), res.second.height());
		cp.box = res.second;
		cp.space = dep.width(c);
		if(res.second.width() <= 0) {
			cp.uv *= 0;
			cp.hTex = mgr_gl.getEmptyTexture();
		} else {
			LaneRaw lraw;
			cplane.rectAlloc(lraw, res.second.width());
			cp.hTex = lraw.hTex;

			// ビットデータをglTexSubImage2Dで書き込む
			auto* u = reinterpret_cast<Texture_Mem*>(lraw.hTex.ref().get());
			u->writeRect(spn::AB_Byte(std::move(res.first)), lraw.rect.width(), lraw.rect.x0, lraw.rect.y0, GL_UNSIGNED_BYTE);

			// UVオフセットを計算
			const auto& sz = cplane.getSurfaceSize();
			float invW = spn::Rcp22Bit(static_cast<float>(sz.width)),
				invH = spn::Rcp22Bit(static_cast<float>(sz.height));

			float h = res.second.height();
			const auto& uvr = lraw.rect;
			cp.uv = spn::RectF(uvr.x0 * invW,
							uvr.x1 * invW,
							uvr.y0 * invH,
							(uvr.y0+h) * invH);
		}
		return &cp;
	}

	// --------------------------- TextObj ---------------------------
	const SPVDecl TextObj::cs_vDecl(
		new VDecl {
			{0, 0, GL_FLOAT, GL_FALSE, 2, (GLuint)VSem::POSITION},
			{0, 8, GL_FLOAT, GL_FALSE, 3, (GLuint)VSem::TEXCOORD0}
		}
	);
	TextObj::TextObj(Face& face, std::u32string&& s): _text(std::move(s)), _coreID(face.coreID), _faceName(face.faceName) {
		_init(face);
	}
	TextObj::TextObj(TextObj&& t): _text(std::move(t._text)), _drawSet(std::move(t._drawSet)),
		_coreID(std::move(t._coreID)), _faceName(std::move(t._faceName)), _rectSize(std::move(t._rectSize)) {}

	void TextObj::_init(Face& face) {
		int height = face.dep.height();
		// CharPosリストの作成
		// 1文字につき4頂点 + インデックス6個
		struct CPair {
			const CharPos*	cp;
			int ofsx, ofsy;
			float timeval;

			CPair(const CharPos* c, int x, int y, float t): cp(c), ofsx(x), ofsy(y), timeval(t) {}
		};
		int ofsx = 0,
			ofsy = -height;
		// テクスチャが複数枚に渡る時はフォント頂点(座標)を使いまわし、UV-tだけを差し替え
		std::map<HTex, std::vector<CPair>>	tpM;
		float dt = spn::Rcp22Bit(_text.length()),
			t = 0;
		for(auto& c : _text) {
			auto* p = face.getCharPos(c);
			// 幾つのテクスチャが要るのかカウントしつつ、フォントを配置
			if(c == U'\n') {
				ofsy -= height;
				ofsx = 0;
			} else {
				if(p->box.width() > 0)
					tpM[p->hTex].emplace_back(p, ofsx, ofsy, t);
				ofsx += p->space;
			}
			t += dt;
		}

		const uint16_t c_index[] = {0,1,2, 2,3,0};
		const std::pair<int,int> c_rectI[] = {{0,2}, {1,2}, {1,3}, {0,3}};

		int nplane = tpM.size();
		_drawSet.resize(nplane);
		_rectSize *= 0;
		auto itr = tpM.begin();
		for(int i=0 ; i<nplane ; i++, ++itr) {
			auto& ds = _drawSet[i];
			std::vector<CPair>& cpl = itr->second;
			int nC = cpl.size(),
				vbase = 0;

			// フォント配置
			spn::ByteBuff vbuff(nC * 4 * sizeof(TextV));
			spn::U16Buff ibuff(nC * 6);
			auto* pv = reinterpret_cast<TextV*>(&vbuff[0]);
			auto* pi = &ibuff[0];
			for(int i=0 ; i<nC ; i++) {
				auto& cp = cpl[i];
				// 頂点生成
				for(auto& r : c_rectI) {
					pv->pos = spn::Vec2(cp.ofsx + cp.cp->box.ar[r.first], cp.ofsy - cp.cp->box.ar[r.second]);
					pv->uvt = spn::Vec3(cp.cp->uv.ar[r.first], cp.cp->uv.ar[r.second], cp.timeval);
					_rectSize.width = std::max(_rectSize.width, pv->pos.x);
					_rectSize.height = std::min(_rectSize.height, pv->pos.y);
					++pv;
				}
				// インデックス生成
				for(auto idx : c_index)
					*pi++ = vbase + idx;
				vbase += 4;
			}

			ds.hTex = itr->first;
			ds.nChar = nC;
			// GLバッファにセット
			ds.hlVb = mgr_gl.makeVBuffer(GL_STATIC_DRAW);
			ds.hlVb.ref()->initData(std::move(vbuff), sizeof(TextV));
			ds.hlIb = mgr_gl.makeIBuffer(GL_STATIC_DRAW);
			ds.hlIb.ref()->initData(std::move(ibuff));
		}
	}

	void TextObj::onCacheLost() {
		// フォントキャッシュを消去
		_drawSet.clear();
	}
	void TextObj::onCacheReset(Face& face) {
		// 再度テキストデータ(CharPosポインタ配列)を作成
		_init(face);
	}
	CCoreID& TextObj::refCoreID() { return _coreID; }
	const SPString& TextObj::getFaceName() const { return _faceName; }
	void TextObj::draw(GLEffect* gle) {
		gle->setVDecl(cs_vDecl);
		auto& str = EUnif::Get(EUnif::Texture::Diffuse);
		auto id = gle->getUniformID(str);
		Assert(Warn, id, u8R"(uniform value %1% is not found)", str)
		if(!id)
			return;
		for(auto& ds : _drawSet) {
			gle->setUniform(*id, ds.hTex);
			gle->setVStream(ds.hlVb.get(), 0);
			gle->setIStream(ds.hlIb.get());
			gle->drawIndexed(GL_TRIANGLES, ds.nChar*6, 0);
		}
	}
	const spn::SizeF& TextObj::getSize() const {
		return _rectSize;
	}

	// --------------------------- FontGen ---------------------------
	namespace {
		SPString ToSp(const std::string& s) { return SPString(new std::string(s)); }
		SPString ToSp(const SPString& s) { return s; }
		const std::string& ToCp(const std::string& s) { return s; }
		const std::string& ToCp(const SPString& s) { return *s; }
	}
	FontGen::FontGen(const spn::PowSize& sfcSize): _sfcSize(sfcSize) {}

	template <class S>
	CCoreID FontGen::_makeCoreID(const S& name, CCoreID cid) {
		auto itr = std::find(_faceL.begin(), _faceL.end(), ToCp(name));
		if(itr == _faceL.end()) {
			// 新しくFaceを作成
			cid.at<CCoreID::FaceID>() = static_cast<int>(_faceL.size());
			_faceL.emplace_back(ToSp(name), _sfcSize, cid, _fontMap);
		} else {
			cid.at<CCoreID::FaceID>() = itr - _faceL.begin();
		}
		return cid;
	}

	CCoreID FontGen::makeCoreID(const std::string& name, CCoreID cid) {
		return _makeCoreID(name, cid);
	}
	CCoreID FontGen::makeCoreID(const SPString& name, CCoreID cid) {
		return _makeCoreID(name, cid);
	}
	Face& FontGen::_getArray(CCoreID cid) {
		// FaceList線形探索
		auto itr = std::find(_faceL.begin(), _faceL.end(), cid);
		AssertP(Trap, itr != _faceL.end())
		return *itr;
	}
	void FontGen::clearCache(bool bRestore) {
		// あくまでもキャッシュのクリアなので文字列データ等は削除しない
		_fontMap.clear();
		_faceL.clear();
		// 文字列クラスのキャッシュも破棄
		for(auto& text : *this)
			text.onCacheLost();

		if(bRestore) {
			for(auto& text: *this) {
				// FaceIDは使わず再度Face参照する
				auto& c = text.refCoreID();
				// TextからSPの名前を取り出してFaceIDを更新
				c = makeCoreID(text.getFaceName(), c);
				text.onCacheReset(_getArray(c));
			}
		}
	}
	std::u32string FontGen::_MakeTextTag(CCoreID cid, const std::u32string& s) {
		// ハンドルキー = CCoreIDの64bit数値 + _ + 文字列
		std::basic_stringstream<char32_t>	ss;
		ss << boost::lexical_cast<std::u32string>(cid.value()) << U'_' << s;
		return ss.str();
	}
}
<commit_msg>fontgen: FaceIDによるFont生成クラスの探索を修正<commit_after>#include "font.hpp"
#include "lane.hpp"
#include "glx.hpp"
#include "sys_uniform.hpp"

namespace rs {
	// --------------------------- Face ---------------------------
	namespace {
		// 最低サイズ2bits, Layer1=4bits, Layer0=6bits = 12bits(4096)
		using LAlloc = LaneAlloc<6,4,2>;
	}
	// フォントのHeightとラインのHeightは違う！
	Face::Face(const SPString& name, const spn::PowSize& sfcSize, CCoreID cid, FontChMap& m):
		faceName(name), dep(*name, cid), coreID(cid), cplane(sfcSize, dep.height(), UPLaneAlloc(new LAlloc())), fontMap(m) {}
	Face::Face(Face&& f):
		faceName(std::move(f.faceName)), dep(std::move(f.dep)), coreID(std::move(f.coreID)), cplane(std::move(f.cplane)), fontMap(f.fontMap) {}
	bool Face::operator != (const std::string& name) const {
		return !(this->operator == (name));
	}
	bool Face::operator ==(const std::string& name) const {
		return *faceName == name;
	}
	bool Face::operator != (CCoreID cid) const {
		return !(this->operator == (cid));
	}
	bool Face::operator == (CCoreID cid) const {
		return coreID == cid;
	}
	const CharPos* Face::getCharPos(char32_t c) {
		CharID chID(c, coreID);
		// キャッシュが既にあればそれを使う
		auto itr = fontMap.find(chID);
		if(itr != fontMap.end())
			return &itr->second;

		// CharMapにエントリを作る
		CharPos& cp = fontMap[chID];
		// Dependクラスから文字のビットデータを取得
		auto res = dep.getChara(c);
		// この時点では1ピクセル8bitなので、32bitRGBAに展開
		res.first = Convert8Bit_Packed32Bit(&res.first[0], res.second.width(), res.second.width(), res.second.height());
		cp.box = res.second;
		cp.space = dep.width(c);
		if(res.second.width() <= 0) {
			cp.uv *= 0;
			cp.hTex = mgr_gl.getEmptyTexture();
		} else {
			LaneRaw lraw;
			cplane.rectAlloc(lraw, res.second.width());
			cp.hTex = lraw.hTex;

			// ビットデータをglTexSubImage2Dで書き込む
			auto* u = reinterpret_cast<Texture_Mem*>(lraw.hTex.ref().get());
			u->writeRect(spn::AB_Byte(std::move(res.first)), lraw.rect.width(), lraw.rect.x0, lraw.rect.y0, GL_UNSIGNED_BYTE);

			// UVオフセットを計算
			const auto& sz = cplane.getSurfaceSize();
			float invW = spn::Rcp22Bit(static_cast<float>(sz.width)),
				invH = spn::Rcp22Bit(static_cast<float>(sz.height));

			float h = res.second.height();
			const auto& uvr = lraw.rect;
			cp.uv = spn::RectF(uvr.x0 * invW,
							uvr.x1 * invW,
							uvr.y0 * invH,
							(uvr.y0+h) * invH);
		}
		return &cp;
	}

	// --------------------------- TextObj ---------------------------
	const SPVDecl TextObj::cs_vDecl(
		new VDecl {
			{0, 0, GL_FLOAT, GL_FALSE, 2, (GLuint)VSem::POSITION},
			{0, 8, GL_FLOAT, GL_FALSE, 3, (GLuint)VSem::TEXCOORD0}
		}
	);
	TextObj::TextObj(Face& face, std::u32string&& s): _text(std::move(s)), _coreID(face.coreID), _faceName(face.faceName) {
		_init(face);
	}
	TextObj::TextObj(TextObj&& t): _text(std::move(t._text)), _drawSet(std::move(t._drawSet)),
		_coreID(std::move(t._coreID)), _faceName(std::move(t._faceName)), _rectSize(std::move(t._rectSize)) {}

	void TextObj::_init(Face& face) {
		int height = face.dep.height();
		// CharPosリストの作成
		// 1文字につき4頂点 + インデックス6個
		struct CPair {
			const CharPos*	cp;
			int ofsx, ofsy;
			float timeval;

			CPair(const CharPos* c, int x, int y, float t): cp(c), ofsx(x), ofsy(y), timeval(t) {}
		};
		int ofsx = 0,
			ofsy = -height;
		// テクスチャが複数枚に渡る時はフォント頂点(座標)を使いまわし、UV-tだけを差し替え
		std::map<HTex, std::vector<CPair>>	tpM;
		float dt = spn::Rcp22Bit(_text.length()),
			t = 0;
		for(auto& c : _text) {
			auto* p = face.getCharPos(c);
			// 幾つのテクスチャが要るのかカウントしつつ、フォントを配置
			if(c == U'\n') {
				ofsy -= height;
				ofsx = 0;
			} else {
				if(p->box.width() > 0)
					tpM[p->hTex].emplace_back(p, ofsx, ofsy, t);
				ofsx += p->space;
			}
			t += dt;
		}

		const uint16_t c_index[] = {0,1,2, 2,3,0};
		const std::pair<int,int> c_rectI[] = {{0,2}, {1,2}, {1,3}, {0,3}};

		int nplane = tpM.size();
		_drawSet.resize(nplane);
		_rectSize *= 0;
		auto itr = tpM.begin();
		for(int i=0 ; i<nplane ; i++, ++itr) {
			auto& ds = _drawSet[i];
			std::vector<CPair>& cpl = itr->second;
			int nC = cpl.size(),
				vbase = 0;

			// フォント配置
			spn::ByteBuff vbuff(nC * 4 * sizeof(TextV));
			spn::U16Buff ibuff(nC * 6);
			auto* pv = reinterpret_cast<TextV*>(&vbuff[0]);
			auto* pi = &ibuff[0];
			for(int i=0 ; i<nC ; i++) {
				auto& cp = cpl[i];
				// 頂点生成
				for(auto& r : c_rectI) {
					pv->pos = spn::Vec2(cp.ofsx + cp.cp->box.ar[r.first], cp.ofsy - cp.cp->box.ar[r.second]);
					pv->uvt = spn::Vec3(cp.cp->uv.ar[r.first], cp.cp->uv.ar[r.second], cp.timeval);
					_rectSize.width = std::max(_rectSize.width, pv->pos.x);
					_rectSize.height = std::min(_rectSize.height, pv->pos.y);
					++pv;
				}
				// インデックス生成
				for(auto idx : c_index)
					*pi++ = vbase + idx;
				vbase += 4;
			}

			ds.hTex = itr->first;
			ds.nChar = nC;
			// GLバッファにセット
			ds.hlVb = mgr_gl.makeVBuffer(GL_STATIC_DRAW);
			ds.hlVb.ref()->initData(std::move(vbuff), sizeof(TextV));
			ds.hlIb = mgr_gl.makeIBuffer(GL_STATIC_DRAW);
			ds.hlIb.ref()->initData(std::move(ibuff));
		}
	}

	void TextObj::onCacheLost() {
		// フォントキャッシュを消去
		_drawSet.clear();
	}
	void TextObj::onCacheReset(Face& face) {
		// 再度テキストデータ(CharPosポインタ配列)を作成
		_init(face);
	}
	CCoreID& TextObj::refCoreID() { return _coreID; }
	const SPString& TextObj::getFaceName() const { return _faceName; }
	void TextObj::draw(GLEffect* gle) {
		gle->setVDecl(cs_vDecl);
		auto& str = EUnif::Get(EUnif::Texture::Diffuse);
		auto id = gle->getUniformID(str);
		Assert(Warn, id, u8R"(uniform value %1% is not found)", str)
		if(!id)
			return;
		for(auto& ds : _drawSet) {
			gle->setUniform(*id, ds.hTex);
			gle->setVStream(ds.hlVb.get(), 0);
			gle->setIStream(ds.hlIb.get());
			gle->drawIndexed(GL_TRIANGLES, ds.nChar*6, 0);
		}
	}
	const spn::SizeF& TextObj::getSize() const {
		return _rectSize;
	}

	// --------------------------- FontGen ---------------------------
	namespace {
		SPString ToSp(const std::string& s) { return SPString(new std::string(s)); }
		SPString ToSp(const SPString& s) { return s; }
		const std::string& ToCp(const std::string& s) { return s; }
		const std::string& ToCp(const SPString& s) { return *s; }
	}
	FontGen::FontGen(const spn::PowSize& sfcSize): _sfcSize(sfcSize) {}

	template <class S>
	CCoreID FontGen::_makeCoreID(const S& name, CCoreID cid) {
		auto itr = std::find(_faceL.begin(), _faceL.end(), ToCp(name));
		if(itr == _faceL.end()) {
			// 新しくFaceを作成
			cid.at<CCoreID::FaceID>() = static_cast<int>(_faceL.size());
			_faceL.emplace_back(ToSp(name), _sfcSize, cid, _fontMap);
		} else {
			cid.at<CCoreID::FaceID>() = itr - _faceL.begin();
		}
		return cid;
	}

	CCoreID FontGen::makeCoreID(const std::string& name, CCoreID cid) {
		return _makeCoreID(name, cid);
	}
	CCoreID FontGen::makeCoreID(const SPString& name, CCoreID cid) {
		return _makeCoreID(name, cid);
	}
	Face& FontGen::_getArray(CCoreID cid) {
		// FaceList線形探索
		auto itr = std::find_if(_faceL.begin(), _faceL.end(), [cid](const Face& fc){
			return fc.coreID.at<CCoreID::FaceID>() == cid.at<CCoreID::FaceID>();
		});
		AssertP(Trap, itr != _faceL.end())
		return *itr;
	}
	void FontGen::clearCache(bool bRestore) {
		// あくまでもキャッシュのクリアなので文字列データ等は削除しない
		_fontMap.clear();
		_faceL.clear();
		// 文字列クラスのキャッシュも破棄
		for(auto& text : *this)
			text.onCacheLost();

		if(bRestore) {
			for(auto& text: *this) {
				// FaceIDは使わず再度Face参照する
				auto& c = text.refCoreID();
				// TextからSPの名前を取り出してFaceIDを更新
				c = makeCoreID(text.getFaceName(), c);
				text.onCacheReset(_getArray(c));
			}
		}
	}
	std::u32string FontGen::_MakeTextTag(CCoreID cid, const std::u32string& s) {
		// ハンドルキー = CCoreIDの64bit数値 + _ + 文字列
		std::basic_stringstream<char32_t>	ss;
		ss << boost::lexical_cast<std::u32string>(cid.value()) << U'_' << s;
		return ss.str();
	}
}
<|endoftext|>


<commit_before>/*
 *  repetition.cpp  -  pushbutton and dialog to specify alarm repetition
 *  Program:  kalarm
 *  Copyright © 2004-2007 by David Jarvie <djarvie@kde.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QGroupBox>
#include <QVBoxLayout>
#include <QHBoxLayout>

#include <kdialog.h>
#include <klocale.h>

#include "buttongroup.h"
#include "radiobutton.h"
#include "spinbox.h"
#include "timeperiod.h"
#include "timeselector.h"
#include "repetition.moc"

using namespace KCal;

/*=============================================================================
= Class RepetitionButton
= Button to display the Simple Alarm Repetition dialog.
=============================================================================*/

RepetitionButton::RepetitionButton(const QString& caption, bool waitForInitialisation, QWidget* parent)
	: QPushButton(caption, parent),
	  mDialog(0),
	  mInterval(0),
	  mCount(0),
	  mMaxDuration(-1),
	  mDateOnly(false),
	  mWaitForInit(waitForInitialisation),
	  mReadOnly(false)
{
	setCheckable(true);
	setChecked(false);
	connect(this, SIGNAL(clicked()), SLOT(slotPressed()));
}

void RepetitionButton::set(const Duration& interval, int count)
{
	mInterval = interval;
	mCount    = count;
	setChecked(mInterval && mCount);
}

/******************************************************************************
*  Set the data for the dialog.
*/
void RepetitionButton::set(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	mInterval    = interval;
	mCount       = count;
	mMaxDuration = maxDuration;
	mDateOnly    = dateOnly;
	setChecked(mInterval && mCount);
}

/******************************************************************************
*  Create the alarm repetition dialog.
*  If 'waitForInitialisation' is true, the dialog won't be displayed until set()
*  is called to initialise its data.
*/
void RepetitionButton::activate(bool waitForInitialisation)
{
	if (!mDialog)
		mDialog = new RepetitionDlg(i18nc("@title:window", "Alarm Sub-Repetition"), mReadOnly, this);
	mDialog->set(mInterval, mCount, mDateOnly, mMaxDuration);
	if (waitForInitialisation)
		emit needsInitialisation();     // request dialog initialisation
	else
		displayDialog();    // display the dialog now
}

/******************************************************************************
*  Set the data for the dialog and display it.
*  To be called only after needsInitialisation() has been emitted.
*/
void RepetitionButton::initialise(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	mInterval    = interval;
	mCount       = count;
	mMaxDuration = maxDuration;
	mDateOnly    = dateOnly;
	if (mDialog)
	{
		mDialog->set(interval, count, dateOnly, maxDuration);
		displayDialog();    // display the dialog now
	}
	else
		setChecked(mInterval && mCount);
}

/******************************************************************************
*  Display the simple alarm repetition dialog.
*  Alarm repetition has the following restrictions:
*  1) Not allowed for a repeat-at-login alarm
*  2) For a date-only alarm, the repeat interval must be a whole number of days.
*  3) The overall repeat duration must be less than the recurrence interval.
*/
void RepetitionButton::displayDialog()
{
	bool change = false;
	if (mReadOnly)
	{
		mDialog->setReadOnly(true);
		mDialog->exec();
	}
	else if (mDialog->exec() == QDialog::Accepted)
	{
		mCount    = mDialog->count();
		mInterval = mDialog->interval();
		change = true;
	}
	setChecked(mInterval && mCount);
	delete mDialog;
	mDialog = 0;
	if (change)
		emit changed();   // delete dialog first, or initialise() will redisplay dialog
}

/*=============================================================================
= Class RepetitionDlg
= Simple alarm repetition dialog.
=============================================================================*/

static const int MAX_COUNT = 9999;    // maximum range for count spinbox

RepetitionDlg::RepetitionDlg(const QString& caption, bool readOnly, QWidget* parent)
	: KDialog(parent),
	  mMaxDuration(-1),
	  mDateOnly(false),
	  mReadOnly(readOnly)
{
	setCaption(caption);
	setButtons(Ok|Cancel);
	int spacing = spacingHint();
	QWidget* page = new QWidget(this);
	setMainWidget(page);
	QVBoxLayout* topLayout = new QVBoxLayout(page);
	topLayout->setMargin(0);
	topLayout->setSpacing(spacing);

	mTimeSelector = new TimeSelector(i18nc("@option:check Repeat every 10 minutes", "Repeat every"), QString(),
	                  i18nc("@info:whatsthis", "Instead of the alarm triggering just once at each recurrence, "
	                       "checking this option makes the alarm trigger multiple times at each recurrence."),
	                  i18nc("@info:whatsthis", "Enter the time between repetitions of the alarm"),
	                  true, page);
	mTimeSelector->setFixedSize(mTimeSelector->sizeHint());
	connect(mTimeSelector, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(intervalChanged(const KCal::Duration&)));
	connect(mTimeSelector, SIGNAL(toggled(bool)), SLOT(repetitionToggled(bool)));
	topLayout->addWidget(mTimeSelector, 0, Qt::AlignLeft);

	mButtonBox = new QGroupBox(page);
	topLayout->addWidget(mButtonBox);
	mButtonGroup = new ButtonGroup(mButtonBox);
	connect(mButtonGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(typeClicked()));

	QVBoxLayout* vlayout = new QVBoxLayout(mButtonBox);
	vlayout->setMargin(marginHint());
	vlayout->setSpacing(spacing);
	QHBoxLayout* layout = new QHBoxLayout();
	layout->setMargin(0);
	vlayout->addLayout(layout);
	mCountButton = new RadioButton(i18nc("@option:radio", "Number of repetitions:"), mButtonBox);
	mCountButton->setFixedSize(mCountButton->sizeHint());
	mCountButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify the number of times the alarm should repeat after each recurrence"));
	mButtonGroup->addButton(mCountButton);
	layout->addWidget(mCountButton);
	mCount = new SpinBox(1, MAX_COUNT, mButtonBox);
	mCount->setFixedSize(mCount->sizeHint());
	mCount->setSingleShiftStep(10);
	mCount->setSelectOnStep(false);
	connect(mCount, SIGNAL(valueChanged(int)), SLOT(countChanged(int)));
	mCount->setWhatsThis(i18nc("@info:whatsthis", "Enter the number of times to trigger the alarm after its initial occurrence"));
	layout->addWidget(mCount);
	mCountButton->setFocusWidget(mCount);
	layout->addStretch();

	layout = new QHBoxLayout();
	layout->setMargin(0);
	vlayout->addLayout(layout);
	mDurationButton = new RadioButton(i18nc("@option:radio", "Duration:"), mButtonBox);
	mDurationButton->setFixedSize(mDurationButton->sizeHint());
	mDurationButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify how long the alarm is to be repeated"));
	mButtonGroup->addButton(mDurationButton);
	layout->addWidget(mDurationButton);
	mDuration = new TimePeriod(true, mButtonBox);
	mDuration->setFixedSize(mDuration->sizeHint());
	connect(mDuration, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(durationChanged(const KCal::Duration&)));
	mDuration->setWhatsThis(i18nc("@info:whatsthis", "Enter the length of time to repeat the alarm"));
	layout->addWidget(mDuration);
	mDurationButton->setFocusWidget(mDuration);
	layout->addStretch();

	mCountButton->setChecked(true);
	repetitionToggled(false);
	setReadOnly(mReadOnly);
}

/******************************************************************************
*  Set the state of all controls to reflect the data in the specified alarm.
*/
void RepetitionDlg::set(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	Duration inter = interval;
	if (!interval)
		count = 0;
	else if (!count)
		inter = 0;
	if (dateOnly != mDateOnly)
	{
		mDateOnly = dateOnly;
		mTimeSelector->setDateOnly(mDateOnly);
		mDuration->setDateOnly(mDateOnly);
	}
	mMaxDuration = maxDuration;
	if (mMaxDuration)
	{
		int maxhm = (mMaxDuration > 0) ? mMaxDuration : 9999;
		int maxdw = (mMaxDuration > 0) ? mMaxDuration / 1440 : 9999;
		mTimeSelector->setMaximum(maxhm, maxdw);
		mDuration->setMaximum(maxhm, maxdw);
	}
	if (!mMaxDuration  ||  !count)
		mTimeSelector->setChecked(false);
	else
	{
		TimePeriod::Units units = mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes;
		mTimeSelector->setPeriod(inter, mDateOnly, units);
		bool on = mTimeSelector->isChecked();
		repetitionToggled(on);    // enable/disable controls
		if (on)
			intervalChanged(inter);    // ensure mCount range is set
		mCount->setValue(count);
		mDuration->setPeriod(inter * count, mDateOnly, units);
		mCountButton->setChecked(true);
	}
	mTimeSelector->setEnabled(mMaxDuration);
}

/******************************************************************************
*  Set the read-only status.
*/
void RepetitionDlg::setReadOnly(bool ro)
{
	ro = ro || mReadOnly;
	mTimeSelector->setReadOnly(ro);
	mCountButton->setReadOnly(ro);
	mCount->setReadOnly(ro);
	mDurationButton->setReadOnly(ro);
	mDuration->setReadOnly(ro);
}

/******************************************************************************
*  Get the period between repetitions in minutes.
*/
Duration RepetitionDlg::interval() const
{
	return mTimeSelector->period();
}

/******************************************************************************
*  Set the entered repeat count.
*/
int RepetitionDlg::count() const
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		if (mCountButton->isChecked())
			return mCount->value();
		if (mDurationButton->isChecked())
			return mDuration->period().asSeconds() / interval.asSeconds();
	}
	return 0;    // no repetition
}

/******************************************************************************
*  Called when the time interval widget has changed value.
*  Adjust the maximum repetition count accordingly.
*/
void RepetitionDlg::intervalChanged(const KCal::Duration& interval)
{
	if (mTimeSelector->isChecked())
	{
		mCount->setRange(1, (mMaxDuration >= 0 ? mMaxDuration / (interval.asSeconds()/60) : MAX_COUNT));
		if (mCountButton->isChecked())
			countChanged(mCount->value());
		else
			durationChanged(mDuration->period());
	}
}

/******************************************************************************
*  Called when the count spinbox has changed value.
*  Adjust the duration accordingly.
*/
void RepetitionDlg::countChanged(int count)
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		bool blocked = mDuration->signalsBlocked();
		mDuration->blockSignals(true);
		mDuration->setPeriod(interval * count, mDateOnly,
		                      (mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes));
		mDuration->blockSignals(blocked);
	}
}

/******************************************************************************
*  Called when the duration widget has changed value.
*  Adjust the count accordingly.
*/
void RepetitionDlg::durationChanged(const KCal::Duration& duration)
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		bool blocked = mCount->signalsBlocked();
		mCount->blockSignals(true);
		mCount->setValue(duration.asSeconds() / interval.asSeconds());
		mCount->blockSignals(blocked);
	}
}

/******************************************************************************
*  Called when the time period widget is toggled on or off.
*/
void RepetitionDlg::repetitionToggled(bool on)
{
	if (mMaxDuration == 0)
		on = false;
	mButtonBox->setEnabled(on);
	mCount->setEnabled(on  &&  mCountButton->isChecked());
	mDuration->setEnabled(on  &&  mDurationButton->isChecked());
}

/******************************************************************************
*  Called when one of the count or duration radio buttons is toggled.
*/
void RepetitionDlg::typeClicked()
{
	if (mTimeSelector->isChecked())
	{
		mCount->setEnabled(mCountButton->isChecked());
		mDuration->setEnabled(mDurationButton->isChecked());
	}
}
<commit_msg>Make units selection consistent even when unchecked initially<commit_after>/*
 *  repetition.cpp  -  pushbutton and dialog to specify alarm repetition
 *  Program:  kalarm
 *  Copyright © 2004-2007 by David Jarvie <djarvie@kde.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QGroupBox>
#include <QVBoxLayout>
#include <QHBoxLayout>

#include <kdialog.h>
#include <klocale.h>

#include "buttongroup.h"
#include "radiobutton.h"
#include "spinbox.h"
#include "timeperiod.h"
#include "timeselector.h"
#include "repetition.moc"

using namespace KCal;

/*=============================================================================
= Class RepetitionButton
= Button to display the Simple Alarm Repetition dialog.
=============================================================================*/

RepetitionButton::RepetitionButton(const QString& caption, bool waitForInitialisation, QWidget* parent)
	: QPushButton(caption, parent),
	  mDialog(0),
	  mInterval(0),
	  mCount(0),
	  mMaxDuration(-1),
	  mDateOnly(false),
	  mWaitForInit(waitForInitialisation),
	  mReadOnly(false)
{
	setCheckable(true);
	setChecked(false);
	connect(this, SIGNAL(clicked()), SLOT(slotPressed()));
}

void RepetitionButton::set(const Duration& interval, int count)
{
	mInterval = interval;
	mCount    = count;
	setChecked(mInterval && mCount);
}

/******************************************************************************
*  Set the data for the dialog.
*/
void RepetitionButton::set(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	mInterval    = interval;
	mCount       = count;
	mMaxDuration = maxDuration;
	mDateOnly    = dateOnly;
	setChecked(mInterval && mCount);
}

/******************************************************************************
*  Create the alarm repetition dialog.
*  If 'waitForInitialisation' is true, the dialog won't be displayed until set()
*  is called to initialise its data.
*/
void RepetitionButton::activate(bool waitForInitialisation)
{
	if (!mDialog)
		mDialog = new RepetitionDlg(i18nc("@title:window", "Alarm Sub-Repetition"), mReadOnly, this);
	mDialog->set(mInterval, mCount, mDateOnly, mMaxDuration);
	if (waitForInitialisation)
		emit needsInitialisation();     // request dialog initialisation
	else
		displayDialog();    // display the dialog now
}

/******************************************************************************
*  Set the data for the dialog and display it.
*  To be called only after needsInitialisation() has been emitted.
*/
void RepetitionButton::initialise(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	mInterval    = interval;
	mCount       = count;
	mMaxDuration = maxDuration;
	mDateOnly    = dateOnly;
	if (mDialog)
	{
		mDialog->set(interval, count, dateOnly, maxDuration);
		displayDialog();    // display the dialog now
	}
	else
		setChecked(mInterval && mCount);
}

/******************************************************************************
*  Display the simple alarm repetition dialog.
*  Alarm repetition has the following restrictions:
*  1) Not allowed for a repeat-at-login alarm
*  2) For a date-only alarm, the repeat interval must be a whole number of days.
*  3) The overall repeat duration must be less than the recurrence interval.
*/
void RepetitionButton::displayDialog()
{
	bool change = false;
	if (mReadOnly)
	{
		mDialog->setReadOnly(true);
		mDialog->exec();
	}
	else if (mDialog->exec() == QDialog::Accepted)
	{
		mCount    = mDialog->count();
		mInterval = mDialog->interval();
		change = true;
	}
	setChecked(mInterval && mCount);
	delete mDialog;
	mDialog = 0;
	if (change)
		emit changed();   // delete dialog first, or initialise() will redisplay dialog
}

/*=============================================================================
= Class RepetitionDlg
= Simple alarm repetition dialog.
=============================================================================*/

static const int MAX_COUNT = 9999;    // maximum range for count spinbox

RepetitionDlg::RepetitionDlg(const QString& caption, bool readOnly, QWidget* parent)
	: KDialog(parent),
	  mMaxDuration(-1),
	  mDateOnly(false),
	  mReadOnly(readOnly)
{
	setCaption(caption);
	setButtons(Ok|Cancel);
	int spacing = spacingHint();
	QWidget* page = new QWidget(this);
	setMainWidget(page);
	QVBoxLayout* topLayout = new QVBoxLayout(page);
	topLayout->setMargin(0);
	topLayout->setSpacing(spacing);

	mTimeSelector = new TimeSelector(i18nc("@option:check Repeat every 10 minutes", "Repeat every"), QString(),
	                  i18nc("@info:whatsthis", "Instead of the alarm triggering just once at each recurrence, "
	                       "checking this option makes the alarm trigger multiple times at each recurrence."),
	                  i18nc("@info:whatsthis", "Enter the time between repetitions of the alarm"),
	                  true, page);
	mTimeSelector->setFixedSize(mTimeSelector->sizeHint());
	connect(mTimeSelector, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(intervalChanged(const KCal::Duration&)));
	connect(mTimeSelector, SIGNAL(toggled(bool)), SLOT(repetitionToggled(bool)));
	topLayout->addWidget(mTimeSelector, 0, Qt::AlignLeft);

	mButtonBox = new QGroupBox(page);
	topLayout->addWidget(mButtonBox);
	mButtonGroup = new ButtonGroup(mButtonBox);
	connect(mButtonGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(typeClicked()));

	QVBoxLayout* vlayout = new QVBoxLayout(mButtonBox);
	vlayout->setMargin(marginHint());
	vlayout->setSpacing(spacing);
	QHBoxLayout* layout = new QHBoxLayout();
	layout->setMargin(0);
	vlayout->addLayout(layout);
	mCountButton = new RadioButton(i18nc("@option:radio", "Number of repetitions:"), mButtonBox);
	mCountButton->setFixedSize(mCountButton->sizeHint());
	mCountButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify the number of times the alarm should repeat after each recurrence"));
	mButtonGroup->addButton(mCountButton);
	layout->addWidget(mCountButton);
	mCount = new SpinBox(1, MAX_COUNT, mButtonBox);
	mCount->setFixedSize(mCount->sizeHint());
	mCount->setSingleShiftStep(10);
	mCount->setSelectOnStep(false);
	connect(mCount, SIGNAL(valueChanged(int)), SLOT(countChanged(int)));
	mCount->setWhatsThis(i18nc("@info:whatsthis", "Enter the number of times to trigger the alarm after its initial occurrence"));
	layout->addWidget(mCount);
	mCountButton->setFocusWidget(mCount);
	layout->addStretch();

	layout = new QHBoxLayout();
	layout->setMargin(0);
	vlayout->addLayout(layout);
	mDurationButton = new RadioButton(i18nc("@option:radio", "Duration:"), mButtonBox);
	mDurationButton->setFixedSize(mDurationButton->sizeHint());
	mDurationButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify how long the alarm is to be repeated"));
	mButtonGroup->addButton(mDurationButton);
	layout->addWidget(mDurationButton);
	mDuration = new TimePeriod(true, mButtonBox);
	mDuration->setFixedSize(mDuration->sizeHint());
	connect(mDuration, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(durationChanged(const KCal::Duration&)));
	mDuration->setWhatsThis(i18nc("@info:whatsthis", "Enter the length of time to repeat the alarm"));
	layout->addWidget(mDuration);
	mDurationButton->setFocusWidget(mDuration);
	layout->addStretch();

	mCountButton->setChecked(true);
	repetitionToggled(false);
	setReadOnly(mReadOnly);
}

/******************************************************************************
*  Set the state of all controls to reflect the data in the specified alarm.
*/
void RepetitionDlg::set(const Duration& interval, int count, bool dateOnly, int maxDuration)
{
	Duration inter = interval;
	if (!interval)
		count = 0;
	else if (!count)
		inter = 0;
	if (dateOnly != mDateOnly)
	{
		mDateOnly = dateOnly;
		mTimeSelector->setDateOnly(mDateOnly);
		mDuration->setDateOnly(mDateOnly);
	}
	mMaxDuration = maxDuration;
	if (mMaxDuration)
	{
		int maxhm = (mMaxDuration > 0) ? mMaxDuration : 9999;
		int maxdw = (mMaxDuration > 0) ? mMaxDuration / 1440 : 9999;
		mTimeSelector->setMaximum(maxhm, maxdw);
		mDuration->setMaximum(maxhm, maxdw);
	}
	// Set the units - needed later if the control is unchecked initially.
	TimePeriod::Units units = mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes;
	mTimeSelector->setPeriod(inter, mDateOnly, units);
	if (!mMaxDuration  ||  !count)
		mTimeSelector->setChecked(false);
	else
	{
		bool on = mTimeSelector->isChecked();
		repetitionToggled(on);    // enable/disable controls
		if (on)
			intervalChanged(inter);    // ensure mCount range is set
		mCount->setValue(count);
		mDuration->setPeriod(inter * count, mDateOnly, units);
		mCountButton->setChecked(true);
	}
	mTimeSelector->setEnabled(mMaxDuration);
}

/******************************************************************************
*  Set the read-only status.
*/
void RepetitionDlg::setReadOnly(bool ro)
{
	ro = ro || mReadOnly;
	mTimeSelector->setReadOnly(ro);
	mCountButton->setReadOnly(ro);
	mCount->setReadOnly(ro);
	mDurationButton->setReadOnly(ro);
	mDuration->setReadOnly(ro);
}

/******************************************************************************
*  Get the period between repetitions in minutes.
*/
Duration RepetitionDlg::interval() const
{
	return mTimeSelector->period();
}

/******************************************************************************
*  Set the entered repeat count.
*/
int RepetitionDlg::count() const
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		if (mCountButton->isChecked())
			return mCount->value();
		if (mDurationButton->isChecked())
			return mDuration->period().asSeconds() / interval.asSeconds();
	}
	return 0;    // no repetition
}

/******************************************************************************
*  Called when the time interval widget has changed value.
*  Adjust the maximum repetition count accordingly.
*/
void RepetitionDlg::intervalChanged(const KCal::Duration& interval)
{
	if (mTimeSelector->isChecked())
	{
		mCount->setRange(1, (mMaxDuration >= 0 ? mMaxDuration / (interval.asSeconds()/60) : MAX_COUNT));
		if (mCountButton->isChecked())
			countChanged(mCount->value());
		else
			durationChanged(mDuration->period());
	}
}

/******************************************************************************
*  Called when the count spinbox has changed value.
*  Adjust the duration accordingly.
*/
void RepetitionDlg::countChanged(int count)
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		bool blocked = mDuration->signalsBlocked();
		mDuration->blockSignals(true);
		mDuration->setPeriod(interval * count, mDateOnly,
		                      (mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes));
		mDuration->blockSignals(blocked);
	}
}

/******************************************************************************
*  Called when the duration widget has changed value.
*  Adjust the count accordingly.
*/
void RepetitionDlg::durationChanged(const KCal::Duration& duration)
{
	Duration interval = mTimeSelector->period();
	if (interval)
	{
		bool blocked = mCount->signalsBlocked();
		mCount->blockSignals(true);
		mCount->setValue(duration.asSeconds() / interval.asSeconds());
		mCount->blockSignals(blocked);
	}
}

/******************************************************************************
*  Called when the time period widget is toggled on or off.
*/
void RepetitionDlg::repetitionToggled(bool on)
{
	if (mMaxDuration == 0)
		on = false;
	mButtonBox->setEnabled(on);
	mCount->setEnabled(on  &&  mCountButton->isChecked());
	mDuration->setEnabled(on  &&  mDurationButton->isChecked());
}

/******************************************************************************
*  Called when one of the count or duration radio buttons is toggled.
*/
void RepetitionDlg::typeClicked()
{
	if (mTimeSelector->isChecked())
	{
		mCount->setEnabled(mCountButton->isChecked());
		mDuration->setEnabled(mDurationButton->isChecked());
	}
}
<|endoftext|>


<commit_before>#include "display.h"
#include "lr35902.h"
#include "memory.h"

void Display::update(uint cycles)
{
  scanline_counter -= cycles;

  if (scanline_counter <= 0)
  {
    scanline_counter += cycles_per_scanline;

    u8 scanline = memory.get8(Memory::IO::LY) + 1;
    if (scanline > 153)
    {
      scanline = 0;
    }
    memory.direct_io_write8(Memory::IO::LY, scanline);

    if (scanline < 144)
    {
      draw_scanline();
    }
    else if (scanline == 144)
    {
      cpu.raise_interrupt(LR35902::Interrupt::VBLANK);
    }
  }
}

void Display::draw_scanline()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);

  if (LCDC & (1<<0))
  {
    draw_tiles();
  }
  if (LCDC & (1<<1))
  {
    draw_sprites();
  }
}

void Display::draw_tiles()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  uint base_tile_map_addr;
  if (LCDC & (1<<6))
  {
    // 0x9C00 - 9FFF
    base_tile_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_tile_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  u8 SCY = memory.get8(Memory::IO::SCY);
  u8 SCX = memory.get8(Memory::IO::SCX);

  uint background_y = (SCY + LY)%256;
  uint tile_row = background_y/8;
  uint tile_y = background_y%8;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint background_x = (SCX + screenx)%256;
    uint tile_col = background_x/8;
    uint tile_x = background_x%8;

    uint tile_map_addr = base_tile_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // TODO select the correct data from the tile
    uint tile_byte = tile_y*2 + tile_x/4;
    u8 pixel = memory.get8(tile_data_addr + tile_byte);

    framebuffer[LY][screenx] = {pixel, 0, 0};
  }
}

void Display::draw_sprites()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
}
<commit_msg>Display: Draw tiles correctly (not flipped and doubled any more)<commit_after>#include "display.h"
#include "lr35902.h"
#include "memory.h"

void Display::update(uint cycles)
{
  scanline_counter -= cycles;

  if (scanline_counter <= 0)
  {
    scanline_counter += cycles_per_scanline;

    u8 scanline = memory.get8(Memory::IO::LY) + 1;
    if (scanline > 153)
    {
      scanline = 0;
    }
    memory.direct_io_write8(Memory::IO::LY, scanline);

    if (scanline < 144)
    {
      draw_scanline();
    }
    else if (scanline == 144)
    {
      cpu.raise_interrupt(LR35902::Interrupt::VBLANK);
    }
  }
}

void Display::draw_scanline()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);

  if (LCDC & (1<<0))
  {
    draw_tiles();
  }
  if (LCDC & (1<<1))
  {
    draw_sprites();
  }
}

void Display::draw_tiles()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
  u8 LY   = memory.get8(Memory::IO::LY);

  uint base_tile_map_addr;
  if (LCDC & (1<<6))
  {
    // 0x9C00 - 9FFF
    base_tile_map_addr = 0x9C00;
  }
  else
  {
    // 0x9800 - 9BFF
    base_tile_map_addr = 0x9800;
  }

  uint base_tile_data_addr;
  bool signed_pattern_numbers;
  if (LCDC & (1<<4))
  {
    // 0x8000 - 0x8FFF
    // Offsets will be unsigned
    base_tile_data_addr = 0x8000;
    signed_pattern_numbers = false;
  }
  else
  {
    // 0x8800 - 0x97FF
    // Offsets will be signed, set addr to middle of range
    base_tile_data_addr = 0x9000;
    signed_pattern_numbers = true;
  }

  u8 SCY = memory.get8(Memory::IO::SCY);
  u8 SCX = memory.get8(Memory::IO::SCX);

  uint background_y = (SCY + LY)%256;
  uint tile_row = background_y/8;
  uint tile_y = background_y%8;

  for (uint screenx=0; screenx<width; screenx++)
  {
    uint background_x = (SCX + screenx)%256;
    uint tile_col = background_x/8;
    uint tile_x = background_x%8;

    // Tile map is 32x32 tiles, with 1 byte per tile
    uint tile_map_addr = base_tile_map_addr + tile_row*32 + tile_col;
    u8 tile_num = memory.get8(tile_map_addr);

    // Tile data is 32x32 tiles, with 16 byte per tile
    uint tile_data_addr;
    if (signed_pattern_numbers)
    {
      tile_data_addr = base_tile_data_addr + ((s8)tile_num)*16;
    }
    else
    {
      tile_data_addr = base_tile_data_addr + ((u8)tile_num)*16;
    }

    // Tiles are 8x8 pixels, with 2 bits per pixel
    // i.e 2 bytes per line and 4 pixels per byte
    //   The 2 bits per pixel aren't adjacent, they are in the
    //   same position in each of the 2 bytes for their line.
    uint tile_byte_offset = tile_y*2;
    u8 tile_byte1 = memory.get16(tile_data_addr + tile_byte_offset);
    u8 tile_byte2 = memory.get16(tile_data_addr + tile_byte_offset + 1);

    uint bit = 7 - tile_x;
    u8 pixel = (tile_byte1 >> bit) & 0x1;
    pixel   |= ((tile_byte2 >> bit) & 0x1) << 1;
    pixel *= 0xff/3;

    framebuffer[LY][screenx] = {pixel, 0, 0};
  }
}

void Display::draw_sprites()
{
  u8 LCDC = memory.get8(Memory::IO::LCDC);
}
<|endoftext|>