/********************************************************************** * File: pdblock.cpp * Description: PDBLK member functions and iterator functions. * Author: Ray Smith * Created: Fri Mar 15 09:41:28 GMT 1991 * * (C) Copyright 1991, Hewlett-Packard Ltd. ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** http://www.apache.org/licenses/LICENSE-2.0 ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. * **********************************************************************/ #include "pdblock.h" #include #include // std::unique_ptr #include "allheaders.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif #define BLOCK_LABEL_HEIGHT 150 //char height of block id const ERRCODE BADBLOCKLINE = "Y coordinate in block out of bounds"; const ERRCODE LOSTBLOCKLINE = "Can't find rectangle for line"; CLISTIZE (PDBLK) /********************************************************************** * PDBLK::PDBLK * * Constructor for a simple rectangular block. **********************************************************************/ PDBLK::PDBLK ( //rectangular block int16_t xmin, //bottom left int16_t ymin, int16_t xmax, //top right int16_t ymax): box (ICOORD (xmin, ymin), ICOORD (xmax, ymax)) { //boundaries ICOORDELT_IT left_it = &leftside; ICOORDELT_IT right_it = &rightside; hand_poly = nullptr; left_it.set_to_list (&leftside); right_it.set_to_list (&rightside); //make default box left_it.add_to_end (new ICOORDELT (xmin, ymin)); left_it.add_to_end (new ICOORDELT (xmin, ymax)); right_it.add_to_end (new ICOORDELT (xmax, ymin)); right_it.add_to_end (new ICOORDELT (xmax, ymax)); index_ = 0; } /********************************************************************** * PDBLK::set_sides * * Sets left and right vertex lists **********************************************************************/ void PDBLK::set_sides( //set vertex lists ICOORDELT_LIST *left, //left vertices ICOORDELT_LIST *right //right vertices ) { //boundaries ICOORDELT_IT left_it = &leftside; ICOORDELT_IT right_it = &rightside; leftside.clear(); left_it.move_to_first(); left_it.add_list_before(left); rightside.clear(); right_it.move_to_first(); right_it.add_list_before(right); } /********************************************************************** * PDBLK::contains * * Return TRUE if the given point is within the block. **********************************************************************/ bool PDBLK::contains( //test containment ICOORD pt //point to test ) { BLOCK_RECT_IT it = this; //rectangle iterator ICOORD bleft, tright; //corners of rectangle for (it.start_block(); !it.cycled_rects(); it.forward()) { //get rectangle it.bounding_box (bleft, tright); //inside rect if (pt.x() >= bleft.x() && pt.x() <= tright.x() && pt.y() >= bleft.y() && pt.y() <= tright.y()) return true; //is inside } return false; //not inside } /********************************************************************** * PDBLK::move * * Reposition block **********************************************************************/ void PDBLK::move( // reposition block const ICOORD vec // by vector ) { ICOORDELT_IT it(&leftside); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) *(it.data ()) += vec; it.set_to_list (&rightside); for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) *(it.data ()) += vec; box.move (vec); } // Returns a binary Pix mask with a 1 pixel for every pixel within the // block. Rotates the coordinate system by rerotation prior to rendering. Pix* PDBLK::render_mask(const FCOORD& rerotation, TBOX* mask_box) { TBOX rotated_box(box); rotated_box.rotate(rerotation); Pix* pix = pixCreate(rotated_box.width(), rotated_box.height(), 1); if (hand_poly != nullptr) { // We are going to rotate, so get a deep copy of the points and // make a new POLY_BLOCK with it. ICOORDELT_LIST polygon; polygon.deep_copy(hand_poly->points(), ICOORDELT::deep_copy); POLY_BLOCK image_block(&polygon, hand_poly->isA()); image_block.rotate(rerotation); // Block outline is a polygon, so use a PB_LINE_IT to get the // rasterized interior. (Runs of interior pixels on a line.) PB_LINE_IT *lines = new PB_LINE_IT(&image_block); for (int y = box.bottom(); y < box.top(); ++y) { const std::unique_ptr segments( lines->get_line(y)); if (!segments->empty()) { ICOORDELT_IT s_it(segments.get()); // Each element of segments is a start x and x size of the // run of interior pixels. for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) { int start = s_it.data()->x(); int xext = s_it.data()->y(); // Set the run of pixels to 1. pixRasterop(pix, start - rotated_box.left(), rotated_box.height() - 1 - (y - rotated_box.bottom()), xext, 1, PIX_SET, nullptr, 0, 0); } } } delete lines; } else { // Just fill the whole block as there is only a bounding box. pixRasterop(pix, 0, 0, rotated_box.width(), rotated_box.height(), PIX_SET, nullptr, 0, 0); } if (mask_box != nullptr) *mask_box = rotated_box; return pix; } /********************************************************************** * PDBLK::plot * * Plot the outline of a block in the given colour. **********************************************************************/ #ifndef GRAPHICS_DISABLED void PDBLK::plot( //draw outline ScrollView* window, //window to draw in int32_t serial, //serial number ScrollView::Color colour //colour to draw in ) { ICOORD startpt; //start of outline ICOORD endpt; //end of outline ICOORD prevpt; //previous point ICOORDELT_IT it = &leftside; //iterator //set the colour window->Pen(colour); window->TextAttributes("Times", BLOCK_LABEL_HEIGHT, false, false, false); if (hand_poly != nullptr) { hand_poly->plot(window, serial); } else if (!leftside.empty ()) { startpt = *(it.data ()); //bottom left corner // tprintf("Block %d bottom left is (%d,%d)\n", // serial,startpt.x(),startpt.y()); char temp_buff[34]; #if defined(__UNIX__) || defined(MINGW) snprintf(temp_buff, sizeof(temp_buff), "%" PRId32, serial); #else ultoa (serial, temp_buff, 10); #endif window->Text(startpt.x (), startpt.y (), temp_buff); window->SetCursor(startpt.x (), startpt.y ()); do { prevpt = *(it.data ()); //previous point it.forward (); //move to next point //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); } while (!it.at_last ()); //until end of list endpt = *(it.data ()); //end point //other side of boundary window->SetCursor(startpt.x (), startpt.y ()); it.set_to_list (&rightside); prevpt = startpt; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { //draw round corner window->DrawTo(prevpt.x (), it.data ()->y ()); window->DrawTo(it.data ()->x (), it.data ()->y ()); prevpt = *(it.data ()); //previous point } //close boundary window->DrawTo(endpt.x(), endpt.y()); } } #endif /********************************************************************** * PDBLK::operator= * * Assignment - duplicate the block structure, but with an EMPTY row list. **********************************************************************/ PDBLK & PDBLK::operator= ( //assignment const PDBLK & source //from this ) { // this->ELIST_LINK::operator=(source); if (!leftside.empty ()) leftside.clear (); if (!rightside.empty ()) rightside.clear (); leftside.deep_copy(&source.leftside, &ICOORDELT::deep_copy); rightside.deep_copy(&source.rightside, &ICOORDELT::deep_copy); box = source.box; return *this; } /********************************************************************** * BLOCK_RECT_IT::BLOCK_RECT_IT * * Construct a block rectangle iterator. **********************************************************************/ BLOCK_RECT_IT::BLOCK_RECT_IT ( //iterate rectangles PDBLK * blkptr //from block ):left_it (&blkptr->leftside), right_it (&blkptr->rightside) { block = blkptr; //remember block //non empty list if (!blkptr->leftside.empty ()) { start_block(); //ready for iteration } } /********************************************************************** * BLOCK_RECT_IT::set_to_block * * Start a new block. **********************************************************************/ void BLOCK_RECT_IT::set_to_block( //start (new) block PDBLK *blkptr) { //block to start block = blkptr; //remember block //set iterators left_it.set_to_list (&blkptr->leftside); right_it.set_to_list (&blkptr->rightside); if (!blkptr->leftside.empty ()) start_block(); //ready for iteration } /********************************************************************** * BLOCK_RECT_IT::start_block * * Restart a block. **********************************************************************/ void BLOCK_RECT_IT::start_block() { //start (new) block left_it.move_to_first (); right_it.move_to_first (); left_it.mark_cycle_pt (); right_it.mark_cycle_pt (); ymin = left_it.data ()->y (); //bottom of first box ymax = left_it.data_relative (1)->y (); if (right_it.data_relative (1)->y () < ymax) //smallest step ymax = right_it.data_relative (1)->y (); } /********************************************************************** * BLOCK_RECT_IT::forward * * Move to the next rectangle in the block. **********************************************************************/ void BLOCK_RECT_IT::forward() { //next rectangle if (!left_it.empty ()) { //non-empty list if (left_it.data_relative (1)->y () == ymax) left_it.forward (); //move to meet top if (right_it.data_relative (1)->y () == ymax) right_it.forward (); //last is special if (left_it.at_last () || right_it.at_last ()) { left_it.move_to_first (); //restart right_it.move_to_first (); //now at bottom ymin = left_it.data ()->y (); } else { ymin = ymax; //new bottom } //next point ymax = left_it.data_relative (1)->y (); if (right_it.data_relative (1)->y () < ymax) //least step forward ymax = right_it.data_relative (1)->y (); } } /********************************************************************** * BLOCK_LINE_IT::get_line * * Get the the start and width of a line in the block. **********************************************************************/ int16_t BLOCK_LINE_IT::get_line( //get a line int16_t y, //line to get int16_t &xext //output extent ) { ICOORD bleft; //bounding box ICOORD tright; //of block & rect //get block box block->bounding_box (bleft, tright); if (y < bleft.y () || y >= tright.y ()) { // block->print(stderr,FALSE); BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); } //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) { //get rectangle box rect_it.bounding_box (bleft, tright); //inside rectangle if (y >= bleft.y () && y < tright.y ()) { //width of line xext = tright.x () - bleft.x (); return bleft.x (); //start of line } } LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y); return 0; //dummy to stop warning }