Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set scale of zoomed parent image to child image in case of overlap in paperjs #2059

Open
laxman83 opened this issue Jan 4, 2024 · 1 comment

Comments

@laxman83
Copy link

laxman83 commented Jan 4, 2024

I facing the issue of fit the overlap the child image in parent zoomed image area. There is one parent layer of raster image . I am loading first parent image then zoom in specific area and capture the co-ordinate and get image from backend (Here the size of both image is same (like width: 1047 and height: 400 ). Before sending the request to backend i captured zoom event and pan event as well (Inside zoom pan operation ) and when i get image response i set previous zoom or pan to parent and the overlap the child image using updated paperjs view center co-ordinate but the scale of parent zoomed image and child image are not matched as per expectations. same issue in panning also.
Parent image

private onUpdateCurrentImage() {
    if (
      this.imageData &&
      this.imageData.regionsImage &&
      this.imageData.regionsImage.data
    ) {
      this.layerImage.activate();
      this.layerImage.removeChildren();
      this.paperScope.view.update();

      const base64Image =
        this.cfgService.isScrollPreviousValue && this.isPrint
          ? this.cfgService.copyOfFullRegionImage[0]?.regionsImage?.data
          : this.imageData.regionsImage.data;
      const regionCordinates = new RegionCo_Ordinates();
      // Create the raster:
      this.imageOnScreen = new paperjs.Raster(base64Image);

      // Axes in UI are in mm, but image in pixel -> tell image on screen how to map scalings
      if (this.isPrint) {
        //Get Raster scale factor as per image position like ('Mirror/Flip/Transposed')
        const [scaleFactor1, scaleFactor2] =
          regionCordinates.getRasterScaleFactor(this.imageData);
        this.imageOnScreen.scale(scaleFactor1, scaleFactor2);
      } else {
        this.imageOnScreen.scale(
          this.imageData.regionsImage.resolutionMmPpxX,
          -this.imageData.regionsImage.resolutionMmPpxY
        ); // Flip Y because zero is at bottom
      }

      // !!! MUST set width and height here manually !!!
      if (this.isPrint) {
        this.imageOnScreen.width = this.cfgService.copyOfFullRegionImage[0]
          ?.regionsImage?.isTransposed
          ? this.cfgService.copyOfFullRegionImage[0]?.regionsImage.imageSizeYpx
          : this.cfgService.copyOfFullRegionImage[0]?.regionsImage.imageSizeXpx;
        this.imageOnScreen.height = this.cfgService.copyOfFullRegionImage[0]
          ?.regionsImage?.isTransposed
          ? this.cfgService.copyOfFullRegionImage[0]?.regionsImage.imageSizeXpx
          : this.cfgService.copyOfFullRegionImage[0]?.regionsImage.imageSizeYpx;
      } else {
        this.imageOnScreen.width = this.imageData.regionsImage.imageSizeXpx;
        this.imageOnScreen.height = this.imageData.regionsImage.imageSizeYpx;
      }
      // Center image coordinates
      const offsetCenter = new paperjs.Point(
        (this.imageData.regionsImage.resolutionMmPpxX *
          this.imageOnScreen.width) /
          2.0 +
          this.imageData.regionsImage.zeroXmm,
        (this.imageData.regionsImage.resolutionMmPpxY *
          this.imageOnScreen.height) /
          2.0 +
          this.imageData.regionsImage.zeroYmm
      );

      // center image and ruler to middle of image
      this.imageOnScreen.position = offsetCenter;
      this.paperScope.view.center = offsetCenter;
      this.initialCenter = this.paperScope.view.center.clone();
      if (this.editorMode === EditorMode.PixelMask) {
        this.imageOnScreen = this.imageOnScreen.rasterize();
      }

      const canv = document.getElementById(this.myIdCanvas) as any;
      if (canv) {
        const zoomX =
          this.imageOnScreen.width > 0
            ? canv.width / this.imageOnScreen.width
            : 0.1;
        const zoomY =
          this.imageOnScreen.height > 0
            ? canv.height / this.imageOnScreen.height
            : 0.1;
        const newZoom = Math.min(zoomX, zoomY);
        this.paperScope.view.scaling.x = newZoom;
        // adding this condition to set the scaling on top left corner for print applications
        this.paperScope.view.scaling.y = this.isPrint
          ? regionCordinates.getZoomFactor(newZoom, this.imageData)
          : -newZoom;
        this.xScaling = JSON.parse(
          JSON.stringify(this.paperScope.view.scaling.x)
        );
        this.yScaling = JSON.parse(
          JSON.stringify(this.paperScope.view.scaling.y)
        );
      }
      this.overlapImage();
      this.drawGrid();
      this.setRuler();
      this.paperScope.view.update();
    }
  }

Overlap image

 overlapImage() {
    // Check if there's a second image available
    if (this.cfgService.isScrollPreviousValue && this.isPrint) {
      this.cfgService.pushZoomEvent = false;

      // set the previous scroll position on parent image
      if (this.cfgService?.eventDatla?.length > 0) {
        this.cfgService?.eventDatla.forEach((event) => {
          if (Array.isArray(event) && event?.length > 0) {
            this.paperScope.view.center = event[0]?.viewCenter;
          } else {
            reMouseEvents.onMouseWheel(this, event?.event);
          }
        });
      }

      const currentZoom = this.paperScope.view.scaling.x;
      console.log('current zoom :: ', currentZoom);
      if (currentZoom > 1.1) {
        const secondImageOnScreen = new paperjs.Raster(
          this.imageData.regionsImage.data
        );

       secondImageOnScreen.onLoad = () => {
          secondImageOnScreen.position = this.paperScope.view.center,
          secondImageOnScreen.fitBounds(this.paperScope.view.bounds, true)
        }
        this.imageOnScreen.addChild(secondImageOnScreen);
      }
      this.cfgService.isScrollPreviousValue = JSON.parse(JSON.stringify(false));
    }
  }

Zoom and panning operation

 onMouseDrag(
    event: paperjs.ToolEvent,
    isPanning?: boolean,
    regionEditorVisibiltyConfig?: RegionEditorVisibilityConfig
  ) {
    const lastViewCenter = this.paperScope.view.center;
    this.paperScope.view.center = this.paperScope.view.center.add(
      this.lastMousePoint.subtract(event.point)
    );
    this.lastMousePoint = event.point.add(
      this.paperScope.view.center.subtract(lastViewCenter)
    );
    this.getpreviousPanningFactor(isPanning, regionEditorVisibiltyConfig);
    // finally fit scale
    this.parent.setRuler();
  }

  // #endregion Panning

  // #region Zoom

  onMouseWheel(
    event: any,
    regionEditorVisibiltyConfig?: RegionEditorVisibilityConfig
  ) {
    const mousePosition = new paperjs.Point(event.offsetX, event.offsetY);
    this.changeZoomCentered(
      event.deltaY,
      mousePosition,
      regionEditorVisibiltyConfig
    );
    this.getPreviousZoomEvent(event, regionEditorVisibiltyConfig);
    this.parent.setRuler();
    this.parent.setSizeOfDotsAndHitTolerance();
  }

  private changeZoomCentered(
    deltaY: number,
    mousePos: paperjs.Point,
    regionEditorVisibiltyConfig?: RegionEditorVisibilityConfig
  ) {
    if (!deltaY) {
      return;
    }
    // Zoom
    const view = this.paperScope.view;
    const oldZoom = view.scaling.x;
    const viewPos = view.viewToProject(mousePos);
    const newZoom = this.calcNewZoom(deltaY);
    const regionCordinates = new RegionCo_Ordinates();
    // we shouldn't use view.scaling() and view.zoom simultaneously.
    // it leads to the conflict inside paperjs library
    view.scaling.x = newZoom;
    // adding this conditon for print applcation to move the y scale co-ordinates to the topleft corner
    view.scaling.y = regionEditorVisibiltyConfig?.isApplicationPrint
      ? regionCordinates.getZoomFactor(newZoom, this.parent.imageData)
      : -newZoom;
    // Center
    const oldCenter = view.center;
    const zoomScale = oldZoom / newZoom;
    const centerAdjust = viewPos.subtract(oldCenter);
    const centerAdjustZoomed = centerAdjust.multiply(zoomScale);
    const offset = viewPos.subtract(centerAdjustZoomed).subtract(oldCenter);
    view.center = view.center.add(offset);
  }

  private calcNewZoom(deltaY: number): number {
    let zoom = this.paperScope.view.scaling.x;
    if (deltaY < 0) {
      zoom *= 1.25;
      if (zoom > this.zoomRange.max) zoom = this.zoomRange.max;
    } else {
      zoom /= 1.25;
      if (zoom < this.zoomRange.min) zoom = this.zoomRange.min;
    }
    return zoom;
  }

  // #endregion Zoom

  private getPreviousZoomEvent(
    event: any,
    regionEditorVisibiltyConfig: RegionEditorVisibilityConfig
  ): void {
    if (regionEditorVisibiltyConfig?.isApplicationPrint) {
      if (this.parent.cfgService?.pushZoomEvent) {
        const filterArray = this.parent.cfgService?.eventDatla?.filter(
          (item) => !Array.isArray(item)
        );

        //if event.deltaY 100 then we pop the element from eventDatla array
        if (event.deltaY > 0) {
          if (
            Array.isArray(
              this.parent.cfgService?.eventDatla[
                this.parent.cfgService?.eventDatla?.length - 1
              ]
            )
          ) {
            this.parent.cfgService?.eventDatla.pop();
          }
          console.log(
            'pop aaray :: ',
            JSON.parse(JSON.stringify(this.parent.cfgService.eventDatla))
          );
          this.parent.cfgService?.eventDatla.pop();
          console.log('pop aaray :: ', this.parent.cfgService.eventDatla);
          this.setCurrentCenter(filterArray);
        } else {
          // const length = this.parent.imageData?.regionsImage?.isTransposed
          // ? 13
          // : 12;
          const isZoomPresent = filterArray?.some(
            (event) => event.zoom === this.paperScope.view.scaling.x
          );

          if (!isZoomPresent) {
            //if event.deltaY -100 then we push the element from eventDatla array
            this.parent.cfgService?.eventDatla.push({
              zoom: this.paperScope.view.scaling.x,
              event,
            });
            console.log('push aaray :: ', this.parent.cfgService.eventDatla);
          }
          console.log('outsideIf :: ', this.parent.cfgService.eventDatla);
        }
      }
    }
    if (!this.parent.cfgService?.isScrollPreviousValue) {
      this.parent?.cfgService.isScrollOrZoom(true);
    }
  }

  // get the previous panning paperjs view center
  private getpreviousPanningFactor(
    isPanning?: boolean,
    regionEditorVisibiltyConfig?: RegionEditorVisibilityConfig
  ): void {
    if (
      this.parent.cfgService?.eventDatla?.length > 0 &&
      regionEditorVisibiltyConfig?.isApplicationPrint
    ) {
      const filterArray = this.parent.cfgService?.eventDatla?.filter(
        (item) => !Array.isArray(item)
      );
      if (!isPanning) {
        if (
          Array.isArray(
            this.parent.cfgService?.eventDatla[
              this.parent.cfgService?.eventDatla?.length - 1
            ]
          )
        ) {
          this.parent.cfgService?.eventDatla.pop();
          this.parent.cfgService.eventDatla.push([
            {
              viewCenter: this.paperScope.view.center,
            },
          ]);
        } else {
          this.parent.cfgService.eventDatla.push([
            {
              viewCenter: this.paperScope.view.center,
            },
          ]);
        }
        this.parent?.cfgService.isScrollOrZoom(true);
      }
    }
  }
  // When we zoom out then get latest paperjs view center
  private setCurrentCenter(filterArray): void {
    if (this.parent.cfgService?.eventDatla?.length > 0) {
      if (
        Array.isArray(
          this.parent.cfgService?.eventDatla[
            this.parent.cfgService?.eventDatla?.length - 1
          ]
        )
      ) {
        this.parent.cfgService?.eventDatla.pop();
        this.parent.cfgService.eventDatla.push([
          {
            viewCenter: this.paperScope.view.center,
          },
        ]);
      } else {
        this.parent.cfgService.eventDatla.push([
          {
            viewCenter: this.paperScope.view.center,
          },
        ]);

        if (!filterArray.length) {
          this.parent.cfgService?.eventDatla.pop();
        }
      }
    }
  }

Rular

 updateRuler(
    paperScope: paperjs.PaperScope,
    wndWidth: number,
    wndHeight: number,
    xy_margin: number,
    scaleRatio: ScaleRatio,
    regionVisibilityConf?: RegionEditorVisibilityConfig // (adding the isImageTransform condition for testing purpose after testing we remove it)
  ) {
    this.scaleRatio = scaleRatio;
    this.regionVisibilityConf = regionVisibilityConf; // (adding the isImageTransform condition for testing purpose after testing we remove it)

    const ptMin = new paperjs.Point(0, 0);
    const ptMax = new paperjs.Point(
      wndWidth - xy_margin * 2,
      wndHeight - xy_margin * 2
    );
    const viewPositionMin = paperScope.view.viewToProject(ptMin);
    const viewPositionMax = paperScope.view.viewToProject(ptMax);
    const getMaxScaleRatio = this.getMaxscaleRatioValue();
    if (viewPositionMin && viewPositionMax) {
      this.setRulerScales(viewPositionMin, viewPositionMax, getMaxScaleRatio);
    }
  }

  private setRulerScales(
    viewPosMin: paperjs.Point,
    viewPosMax: paperjs.Point,
    scaleRatio: number
  ) {
    // .domain(data)   <= This is what is written on the Axis
    // .range([a, b]); <= This is where the axis is placed
    const fullImage = this.regionService?.getFullRegionImage();
    // X-Scale
    const dataX = this.isPrint
      ? [viewPosMin.x, viewPosMax.x].map((pixel) => pixel * scaleRatio)
      : [viewPosMin.x, viewPosMax.x];

    this.xScale = d3
      .scaleLinear()
      .domain(dataX)
      .range([this.margin, this.width - this.margin]);

    // Y-Scale
    const dataY = this.isPrint
      ? [viewPosMin.y, viewPosMax.y].map((pixel) => pixel * scaleRatio)
      : [viewPosMin.y, viewPosMax.y];
    this.yScale = d3
      .scaleLinear()
      .domain(dataY)
      .range([this.margin, this.height - this.margin]);

    this.xAxis = this.isPrint
      ? d3.axisTop(this.xScale).tickSizeInner(10)
      : d3.axisBottom(this.xScale).tickSizeInner(10);
    this.yAxis = d3.axisLeft(this.yScale).tickSizeInner(10);
    // Temporarily added null it not tested properly
    this.ruler.selectAll('g.xaxis').call(this.xAxis as any);
    this.ruler.selectAll('g.yaxis').call(this.yAxis as any);
    //set axis label
    this.setAxisLabel(fullImage);

    if (this.isPrint) {
      const obj = {
        'xaxis.range[0]': Math.round(this.xScale.domain()[0]),
        'xaxis.range[1]': Math.round(this.xScale.domain()[1]),
        'yaxis.range[0]': Math.round(this.yScale.domain()[0]),
        'yaxis.range[1]': Math.round(this.yScale.domain()[1]),
      };
      this.regionService.getMinMaxZoomVal(obj);
    }
  }

  // Set axis lable for print application
  setAxisLabel(fullImage?: RegionsImage) {
    if (this.isPrint) {
      //Select and remove exisitng X-axis label
      this.ruler.selectAll('.x-axis-label').remove();

      //Select and remove exisitng Y-axis label
      this.ruler.selectAll('.y-axis-label').remove();
      // Add X-axis label
      this.ruler
        .append('text')
        .attr('class', 'x-axis-label')
        .attr('text-anchor', 'middle') // Center the label horizontally
        .attr('x', this.width / 2) // Position in the middle of the x-axis
        .attr(
          'y',
          this.isPrint ? this.margin / 2 : this.height - this.margin / 2
        ) // Position above or below the x-axis based on 'isPrint'
        .attr('fill', 'white')
        .text(
          `${
            this.isPrint && fullImage?.regionsImage?.isTransposed
              ? this.translateFile.instant(
                  'JobSetup.Code-Reading.Moving direction'
                )
              : this.translateFile.instant(
                  'JobSetup.Code-Reading.Cross direction'
                )
          }`
        );

      this.ruler
        .append('text')
        .attr('class', 'y-axis-label')
        .attr('text-anchor', 'middle') // Center the label vertically
        .attr(
          'x',
          this.isPrint
            ? -(this.margin + this.height / 2)
            : -(this.margin - this.height / 2)
        ) // Position to the left or right of the y-axis based on 'isPrint'
        .attr('y', this.margin - 35) // Position in the middle of the y-axis
        .attr('fill', 'white') // Set the text color to white
        .attr('transform', 'rotate(-90)') // Rotate the label for vertical orientation
        .text(
          `${
            this.isPrint && fullImage?.regionsImage?.isTransposed
              ? this.translateFile.instant(
                  'JobSetup.Code-Reading.Cross direction'
                )
              : this.translateFile.instant(
                  'JobSetup.Code-Reading.Moving direction'
                )
          }`
        );
    }
  }

Can any one help to solve this issue.

I tried set manual scaling to match with parent but it is not set as well i used fitbound() method but there also some scaling issue. not getting expected behaviors.

I want to match overlap child image scale with parent image zoomed scale

Thank you so much advance

@laxman83
Copy link
Author

laxman83 commented Jan 4, 2024

you can see the image not fit inside rectangle .there is left and right space are remaining
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant